agora inbox for pgsql-hackers@postgresql.org
help / color / mirror / Atom feed[PATCH 2/3] Force GIN recheck more accurately
376+ messages / 6 participants
[nested] [flat]
* [PATCH 2/3] Force GIN recheck more accurately
@ 2019-08-01 15:45 Nikita Glukhov <n.gluhov@postgrespro.ru>
0 siblings, 0 replies; 376+ messages in thread
From: Nikita Glukhov @ 2019-08-01 15:45 UTC (permalink / raw)
---
src/backend/access/gin/ginlogic.c | 3 +-
src/backend/access/gin/ginscan.c | 120 ++++++++++++++++++++++++++++++++------
src/include/access/gin.h | 1 +
src/test/regress/expected/gin.out | 97 +++++++++++++++++++++++-------
src/test/regress/sql/gin.sql | 68 ++++++++++++++++++---
5 files changed, 241 insertions(+), 48 deletions(-)
diff --git a/src/backend/access/gin/ginlogic.c b/src/backend/access/gin/ginlogic.c
index 8f85978..7c4805d 100644
--- a/src/backend/access/gin/ginlogic.c
+++ b/src/backend/access/gin/ginlogic.c
@@ -224,7 +224,8 @@ shimTriConsistentFn(GinScanKey key)
void
ginInitConsistentFunction(GinState *ginstate, GinScanKey key)
{
- if (key->searchMode == GIN_SEARCH_MODE_EVERYTHING)
+ if (key->searchMode == GIN_SEARCH_MODE_EVERYTHING ||
+ key->searchMode == GIN_SEARCH_MODE_NOT_NULL)
{
key->boolConsistentFn = trueConsistentFn;
key->triConsistentFn = trueTriConsistentFn;
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 11e7e8e..f612e55 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -129,20 +129,23 @@ ginFillScanEntry(GinScanOpaque so, OffsetNumber attnum,
* Initialize the next GinScanKey using the output from the extractQueryFn
*/
static void
-ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
- StrategyNumber strategy, int32 searchMode,
+ginFillScanKey(GinScanOpaque so, GinScanKey key, bool initHiddenEntries,
+ OffsetNumber attnum, StrategyNumber strategy, int32 searchMode,
Datum query, uint32 nQueryValues,
Datum *queryValues, GinNullCategory *queryCategories,
bool *partial_matches, Pointer *extra_data)
{
- GinScanKey key = &(so->keys[so->nkeys++]);
GinState *ginstate = &so->ginstate;
uint32 nUserQueryValues = nQueryValues;
uint32 i;
+ if (key == NULL)
+ key = &(so->keys[so->nkeys++]);
+
/* Non-default search modes add one "hidden" entry to each key */
- if (searchMode != GIN_SEARCH_MODE_DEFAULT)
+ if (searchMode != GIN_SEARCH_MODE_DEFAULT && initHiddenEntries)
nQueryValues++;
+
key->nentries = nQueryValues;
key->nuserentries = nUserQueryValues;
@@ -200,6 +203,11 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
case GIN_SEARCH_MODE_EVERYTHING:
queryCategory = GIN_CAT_EMPTY_QUERY;
break;
+ case GIN_SEARCH_MODE_NOT_NULL:
+ queryCategory = GIN_CAT_EMPTY_QUERY;
+ /* use GIN_SEARCH_MODE_ALL to skip NULLs */
+ searchMode = GIN_SEARCH_MODE_ALL;
+ break;
default:
elog(ERROR, "unexpected searchMode: %d", searchMode);
queryCategory = 0; /* keep compiler quiet */
@@ -265,6 +273,9 @@ ginNewScanKey(IndexScanDesc scan)
GinScanOpaque so = (GinScanOpaque) scan->opaque;
int i;
bool hasNullQuery = false;
+ int numColsNeedNotNull = 0;
+ bool colNeedsNotNull[INDEX_MAX_KEYS] = {0};
+ bool colImpliesNotNull[INDEX_MAX_KEYS] = {0};
MemoryContext oldCtx;
/*
@@ -298,6 +309,7 @@ ginNewScanKey(IndexScanDesc scan)
bool *nullFlags = NULL;
GinNullCategory *categories;
int32 searchMode = GIN_SEARCH_MODE_DEFAULT;
+ int colno = skey->sk_attno - 1;
/*
* We assume that GIN-indexable operators are strict, so a null query
@@ -311,8 +323,8 @@ ginNewScanKey(IndexScanDesc scan)
/* OK to call the extractQueryFn */
queryValues = (Datum *)
- DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
- so->ginstate.supportCollation[skey->sk_attno - 1],
+ DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[colno],
+ so->ginstate.supportCollation[colno],
skey->sk_argument,
PointerGetDatum(&nQueryValues),
UInt16GetDatum(skey->sk_strategy),
@@ -342,7 +354,32 @@ ginNewScanKey(IndexScanDesc scan)
}
else if (searchMode == GIN_SEARCH_MODE_ALL)
{
- so->forcedRecheck = true;
+ /*
+ * Don't emit ALL key with no entries, check only whether
+ * unconditional recheck is needed.
+ */
+ if (!so->forcedRecheck)
+ {
+ GinScanKeyData key;
+
+ ginFillScanKey(so, &key, false, skey->sk_attno,
+ skey->sk_strategy, searchMode,
+ skey->sk_argument, 0,
+ NULL, NULL, NULL, NULL);
+
+ so->forcedRecheck |= key.triConsistentFn(&key) != GIN_TRUE;
+ }
+
+ /*
+ * Increment the number of columns with NOT NULL constraints
+ * if NOT NULL is not yet implied.
+ */
+ if (!colImpliesNotNull[colno] && !colNeedsNotNull[colno])
+ {
+ colNeedsNotNull[colno] = true;
+ numColsNeedNotNull++;
+ }
+
continue;
}
@@ -373,24 +410,71 @@ ginNewScanKey(IndexScanDesc scan)
}
}
- ginFillScanKey(so, skey->sk_attno,
+ ginFillScanKey(so, NULL, true, skey->sk_attno,
skey->sk_strategy, searchMode,
skey->sk_argument, nQueryValues,
queryValues, categories,
partial_matches, extra_data);
+
+ /*
+ * Current key implies that column is NOT NULL, so decrement the number
+ * of columns with NOT NULL constraints.
+ */
+ colImpliesNotNull[colno] = true;
+
+ if (colNeedsNotNull[colno])
+ {
+ colNeedsNotNull[colno] = false;
+ numColsNeedNotNull--;
+ }
}
- /*
- * If there are no regular scan keys, generate an EVERYTHING scankey to
- * drive a full-index scan.
- */
- if (so->nkeys == 0 && !so->isVoidRes)
+ if (!so->isVoidRes)
{
- hasNullQuery = true;
- ginFillScanKey(so, FirstOffsetNumber,
- InvalidStrategy, GIN_SEARCH_MODE_EVERYTHING,
- (Datum) 0, 0,
- NULL, NULL, NULL, NULL);
+ /*
+ * If there are no regular scan keys, generate an EVERYTHING or
+ * NOT_NULL scankey to drive a full-index scan.
+ */
+ if (so->nkeys == 0)
+ {
+ hasNullQuery = true;
+
+ /* Initialize EVERYTHING key if there are no NOT NULL columns. */
+ if (!numColsNeedNotNull)
+ {
+ ginFillScanKey(so, NULL, true, FirstOffsetNumber,
+ InvalidStrategy, GIN_SEARCH_MODE_EVERYTHING,
+ (Datum) 0, 0, NULL, NULL, NULL, NULL);
+ }
+ else
+ {
+ /*
+ * Initialize only one NOT_NULL key for the first found
+ * NOT NULL column and force recheck if there are more than
+ * one NOT NULL column.
+ */
+ so->forcedRecheck |= numColsNeedNotNull > 1;
+
+ for (i = 0; i < scan->indexRelation->rd_att->natts; i++)
+ {
+ if (colNeedsNotNull[i])
+ {
+ ginFillScanKey(so, NULL, true, i + 1, InvalidStrategy,
+ GIN_SEARCH_MODE_NOT_NULL, (Datum) 0, 0,
+ NULL, NULL, NULL, NULL);
+ break;
+ }
+ }
+ }
+ }
+ else if (numColsNeedNotNull > 0)
+ {
+ /*
+ * We use recheck instead of adding NOT_NULL entries to eliminate
+ * rows with NULL columns.
+ */
+ so->forcedRecheck = true;
+ }
}
/*
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index a8eef5a..069d249 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -34,6 +34,7 @@
#define GIN_SEARCH_MODE_INCLUDE_EMPTY 1
#define GIN_SEARCH_MODE_ALL 2
#define GIN_SEARCH_MODE_EVERYTHING 3 /* for internal use only */
+#define GIN_SEARCH_MODE_NOT_NULL 4 /* for internal use only */
/*
* GinStatsData represents stats data for planner use
diff --git a/src/test/regress/expected/gin.out b/src/test/regress/expected/gin.out
index fb0d29c..5b40691 100644
--- a/src/test/regress/expected/gin.out
+++ b/src/test/regress/expected/gin.out
@@ -38,28 +38,81 @@ vacuum gin_test_tbl;
-- Test optimization of empty queries
create temp table t_gin_test_tbl(i int4[], j int4[]);
create index on t_gin_test_tbl using gin (i, j);
-insert into t_gin_test_tbl select array[100,g], array[200,g]
-from generate_series(1, 10) g;
-insert into t_gin_test_tbl values(array[0,0], null);
+insert into t_gin_test_tbl
+values
+ (null, null),
+ ('{}', null),
+ ('{1}', null),
+ ('{1,2}', null),
+ (null, '{}'),
+ (null, '{10}'),
+ ('{1,2}', '{10}'),
+ ('{2}', '{10}'),
+ ('{1,3}', '{}'),
+ ('{1,1}', '{10}');
set enable_seqscan = off;
-explain
-select * from t_gin_test_tbl where array[0] <@ i;
- QUERY PLAN
---------------------------------------------------------------------------------------
- Bitmap Heap Scan on t_gin_test_tbl (cost=12.03..20.49 rows=4 width=64)
- Recheck Cond: ('{0}'::integer[] <@ i)
- -> Bitmap Index Scan on t_gin_test_tbl_i_j_idx (cost=0.00..12.03 rows=4 width=0)
- Index Cond: (i @> '{0}'::integer[])
-(4 rows)
+explain (analyze, costs off, timing off, summary off)
+select * from t_gin_test_tbl where i @> '{}';
+ QUERY PLAN
+---------------------------------------------------------------------------
+ Bitmap Heap Scan on t_gin_test_tbl (actual rows=7 loops=1)
+ Recheck Cond: (i @> '{}'::integer[])
+ Heap Blocks: exact=1
+ -> Bitmap Index Scan on t_gin_test_tbl_i_j_idx (actual rows=7 loops=1)
+ Index Cond: (i @> '{}'::integer[])
+(5 rows)
-select * from t_gin_test_tbl where array[0] <@ i;
- i | j
--------+---
- {0,0} |
-(1 row)
-
-select * from t_gin_test_tbl where array[0] <@ i and '{}'::int4[] <@ j;
- i | j
----+---
-(0 rows)
+create or replace function explain_query_json(query_sql text)
+returns table (explain_line json)
+language plpgsql as
+$$
+begin
+ return query execute 'EXPLAIN (ANALYZE, FORMAT json) ' || query_sql;
+end;
+$$;
+create or replace function execute_text_query(query_sql text)
+returns setof text
+language plpgsql
+as
+$$
+begin
+ return query execute query_sql;
+end;
+$$;
+-- check number of rows returned by index and removed by recheck
+select
+ query,
+ js->0->'Plan'->'Plans'->0->'Actual Rows' as "return by index",
+ js->0->'Plan'->'Rows Removed by Index Recheck' as "removed by recheck",
+ res as "result"
+from
+ (values
+ ($$ i @> '{}' $$),
+ ($$ j @> '{}' $$),
+ ($$ i @> '{}' and j @> '{}' $$),
+ ($$ i @> '{1}' $$),
+ ($$ i @> '{1}' and j @> '{}' $$),
+ ($$ i @> '{1}' and i @> '{}' and j @> '{}' $$),
+ ($$ j @> '{10}' $$),
+ ($$ j @> '{10}' and i @> '{}' $$),
+ ($$ j @> '{10}' and j @> '{}' and i @> '{}' $$),
+ ($$ i @> '{1}' and j @> '{10}' $$)
+ ) q(query),
+ lateral explain_query_json($$select * from t_gin_test_tbl where $$ || query) js,
+ lateral execute_text_query($$select string_agg((i, j)::text, ' ') from t_gin_test_tbl where $$ || query) res;
+ query | return by index | removed by recheck | result
+-------------------------------------------+-----------------+--------------------+-------------------------------------------------------------------------------
+ i @> '{}' | 7 | 0 | ({},) ({1},) ("{1,2}",) ("{1,2}",{10}) ({2},{10}) ("{1,3}",{}) ("{1,1}",{10})
+ j @> '{}' | 6 | 0 | (,{}) (,{10}) ("{1,2}",{10}) ({2},{10}) ("{1,3}",{}) ("{1,1}",{10})
+ i @> '{}' and j @> '{}' | 7 | 3 | ("{1,2}",{10}) ({2},{10}) ("{1,3}",{}) ("{1,1}",{10})
+ i @> '{1}' | 5 | 0 | ({1},) ("{1,2}",) ("{1,2}",{10}) ("{1,3}",{}) ("{1,1}",{10})
+ i @> '{1}' and j @> '{}' | 5 | 2 | ("{1,2}",{10}) ("{1,3}",{}) ("{1,1}",{10})
+ i @> '{1}' and i @> '{}' and j @> '{}' | 5 | 2 | ("{1,2}",{10}) ("{1,3}",{}) ("{1,1}",{10})
+ j @> '{10}' | 4 | 0 | (,{10}) ("{1,2}",{10}) ({2},{10}) ("{1,1}",{10})
+ j @> '{10}' and i @> '{}' | 4 | 1 | ("{1,2}",{10}) ({2},{10}) ("{1,1}",{10})
+ j @> '{10}' and j @> '{}' and i @> '{}' | 4 | 1 | ("{1,2}",{10}) ({2},{10}) ("{1,1}",{10})
+ i @> '{1}' and j @> '{10}' | 2 | 0 | ("{1,2}",{10}) ("{1,1}",{10})
+(10 rows)
+reset enable_seqscan;
+drop table t_gin_test_tbl;
diff --git a/src/test/regress/sql/gin.sql b/src/test/regress/sql/gin.sql
index aaf9c19..57af762 100644
--- a/src/test/regress/sql/gin.sql
+++ b/src/test/regress/sql/gin.sql
@@ -38,11 +38,65 @@ vacuum gin_test_tbl;
-- Test optimization of empty queries
create temp table t_gin_test_tbl(i int4[], j int4[]);
create index on t_gin_test_tbl using gin (i, j);
-insert into t_gin_test_tbl select array[100,g], array[200,g]
-from generate_series(1, 10) g;
-insert into t_gin_test_tbl values(array[0,0], null);
+insert into t_gin_test_tbl
+values
+ (null, null),
+ ('{}', null),
+ ('{1}', null),
+ ('{1,2}', null),
+ (null, '{}'),
+ (null, '{10}'),
+ ('{1,2}', '{10}'),
+ ('{2}', '{10}'),
+ ('{1,3}', '{}'),
+ ('{1,1}', '{10}');
+
set enable_seqscan = off;
-explain
-select * from t_gin_test_tbl where array[0] <@ i;
-select * from t_gin_test_tbl where array[0] <@ i;
-select * from t_gin_test_tbl where array[0] <@ i and '{}'::int4[] <@ j;
+
+explain (analyze, costs off, timing off, summary off)
+select * from t_gin_test_tbl where i @> '{}';
+
+create or replace function explain_query_json(query_sql text)
+returns table (explain_line json)
+language plpgsql as
+$$
+begin
+ return query execute 'EXPLAIN (ANALYZE, FORMAT json) ' || query_sql;
+end;
+$$;
+
+create or replace function execute_text_query(query_sql text)
+returns setof text
+language plpgsql
+as
+$$
+begin
+ return query execute query_sql;
+end;
+$$;
+
+-- check number of rows returned by index and removed by recheck
+select
+ query,
+ js->0->'Plan'->'Plans'->0->'Actual Rows' as "return by index",
+ js->0->'Plan'->'Rows Removed by Index Recheck' as "removed by recheck",
+ res as "result"
+from
+ (values
+ ($$ i @> '{}' $$),
+ ($$ j @> '{}' $$),
+ ($$ i @> '{}' and j @> '{}' $$),
+ ($$ i @> '{1}' $$),
+ ($$ i @> '{1}' and j @> '{}' $$),
+ ($$ i @> '{1}' and i @> '{}' and j @> '{}' $$),
+ ($$ j @> '{10}' $$),
+ ($$ j @> '{10}' and i @> '{}' $$),
+ ($$ j @> '{10}' and j @> '{}' and i @> '{}' $$),
+ ($$ i @> '{1}' and j @> '{10}' $$)
+ ) q(query),
+ lateral explain_query_json($$select * from t_gin_test_tbl where $$ || query) js,
+ lateral execute_text_query($$select string_agg((i, j)::text, ' ') from t_gin_test_tbl where $$ || query) res;
+
+reset enable_seqscan;
+
+drop table t_gin_test_tbl;
--
2.7.4
--------------B7A985DD4CDB1298897DF1B4
Content-Type: text/x-patch;
name="0003-Avoid-GIN-full-scan-for-non-empty-ALL-keys-v06.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename*0="0003-Avoid-GIN-full-scan-for-non-empty-ALL-keys-v06.patch"
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH v30 06/11] Add Incremental View Maintenance support
@ 2023-05-31 09:59 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2023-05-31 09:59 UTC (permalink / raw)
In this implementation, AFTER triggers are used to collect
tuplestores containing transition table contents. When multiple tables
are changed, multiple AFTER triggers are invoked, then the final AFTER
trigger performs actual update of the matview. In addition, BEFORE
triggers are also used to handle global information for view
maintenance.
To calculate view deltas, we need both pre-state and post-state of base
tables. Post-update states are available in AFTER trigger, and pre-update
states can be calculated by removing inserted tuples and appending deleted
tuples. Insterted tuples are filtered using the snapshot taken before
table modiication, and deleted tuples are contained in the old transition
table.
Incrementally Maintainable Materialized Views (IMMV) can contain
duplicated tuples.
This patch also allows self-join, simultaneous updates of more than
one base table, and multiple updates of the same base table.
---
src/backend/access/transam/xact.c | 5 +
src/backend/commands/createas.c | 681 ++++++++++++++
src/backend/commands/matview.c | 1466 ++++++++++++++++++++++++++++-
src/include/catalog/pg_proc.dat | 10 +
src/include/commands/createas.h | 4 +
src/include/commands/matview.h | 9 +
6 files changed, 2140 insertions(+), 35 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 70ab6e27a1..0de7ab6abe 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -36,6 +36,7 @@
#include "catalog/pg_enum.h"
#include "catalog/storage.h"
#include "commands/async.h"
+#include "commands/matview.h"
#include "commands/tablecmds.h"
#include "commands/trigger.h"
#include "common/pg_prng.h"
@@ -2819,6 +2820,7 @@ AbortTransaction(void)
AtAbort_Notify();
AtEOXact_RelationMap(false, is_parallel_worker);
AtAbort_Twophase();
+ AtAbort_IVM();
/*
* Advertise the fact that we aborted in pg_xact (assuming that we got as
@@ -5097,6 +5099,9 @@ AbortSubTransaction(void)
pgstat_progress_end_command();
UnlockBuffers();
+ /* Clean up hash entries for incremental view maintenance */
+ AtAbort_IVM();
+
/* Reset WAL record construction state */
XLogResetInsertion();
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 16a2fe65e6..08262100ea 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -32,15 +32,26 @@
#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/namespace.h"
+#include "catalog/index.h"
+#include "catalog/pg_constraint.h"
+#include "catalog/pg_inherits.h"
+#include "catalog/pg_trigger.h"
#include "catalog/toasting.h"
#include "commands/createas.h"
+#include "commands/defrem.h"
#include "commands/matview.h"
#include "commands/prepare.h"
#include "commands/tablecmds.h"
+#include "commands/tablespace.h"
+#include "commands/trigger.h"
#include "commands/view.h"
#include "miscadmin.h"
+#include "optimizer/optimizer.h"
+#include "optimizer/prep.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
+#include "parser/parser.h"
+#include "parser/parsetree.h"
#include "parser/parse_clause.h"
#include "rewrite/rewriteHandler.h"
#include "storage/smgr.h"
@@ -73,6 +84,12 @@ static bool intorel_receive(TupleTableSlot *slot, DestReceiver *self);
static void intorel_shutdown(DestReceiver *self);
static void intorel_destroy(DestReceiver *self);
+static void CreateIvmTriggersOnBaseTablesRecurse(Query *qry, Node *node, Oid matviewOid,
+ Relids *relids, bool ex_lock);
+static void CreateIvmTrigger(Oid relOid, Oid viewOid, int16 type, int16 timing, bool ex_lock);
+static void check_ivm_restriction(Node *node);
+static bool check_ivm_restriction_walker(Node *node, void *context);
+static Bitmapset *get_primary_key_attnos_from_query(Query *query, List **constraintList);
/*
* create_ctas_internal
@@ -282,6 +299,18 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
save_nestlevel = NewGUCNestLevel();
}
+ if (is_matview && into->ivm)
+ {
+ /* check if the query is supported in IMMV definition */
+ if (contain_mutable_functions((Node *) query))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("mutable function is not supported on incrementally maintainable materialized view"),
+ errhint("functions must be marked IMMUTABLE")));
+
+ check_ivm_restriction((Node *) query);
+ }
+
if (into->skipData)
{
/*
@@ -358,6 +387,27 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* Restore userid and security context */
SetUserIdAndSecContext(save_userid, save_sec_context);
+
+ if (into->ivm)
+ {
+ Oid matviewOid = address.objectId;
+ Relation matviewRel = table_open(matviewOid, NoLock);
+
+ /*
+ * Mark relisivm field, if it's a matview and into->ivm is true.
+ */
+ SetMatViewIVMState(matviewRel, true);
+
+ if (!into->skipData)
+ {
+ /* Create an index on incremental maintainable materialized view, if possible */
+ CreateIndexOnIMMV((Query *) into->viewQuery, matviewRel);
+
+ /* Create triggers on incremental maintainable materialized view */
+ CreateIvmTriggersOnBaseTables((Query *) into->viewQuery, matviewOid);
+ }
+ table_close(matviewRel, NoLock);
+ }
}
return address;
@@ -635,3 +685,634 @@ intorel_destroy(DestReceiver *self)
{
pfree(self);
}
+
+/*
+ * CreateIvmTriggersOnBaseTables -- create IVM triggers on all base tables
+ */
+void
+CreateIvmTriggersOnBaseTables(Query *qry, Oid matviewOid)
+{
+ Relids relids = NULL;
+ bool ex_lock = false;
+ RangeTblEntry *rte;
+
+ /* Immediately return if we don't have any base tables. */
+ if (list_length(qry->rtable) < 1)
+ return;
+
+ /*
+ * If the view has more than one base tables, we need an exclusive lock
+ * on the view so that the view would be maintained serially to avoid
+ * the inconsistency that occurs when two base tables are modified in
+ * concurrent transactions. However, if the view has only one table,
+ * we can use a weaker lock.
+ *
+ * The type of lock should be determined here, because if we check the
+ * view definition at maintenance time, we need to acquire a weaker lock,
+ * and upgrading the lock level after this increases probability of
+ * deadlock.
+ */
+
+ rte = list_nth(qry->rtable, 0);
+ if (list_length(qry->rtable) > 1 || rte->rtekind != RTE_RELATION)
+ ex_lock = true;
+
+ CreateIvmTriggersOnBaseTablesRecurse(qry, (Node *)qry, matviewOid, &relids, ex_lock);
+
+ bms_free(relids);
+}
+
+static void
+CreateIvmTriggersOnBaseTablesRecurse(Query *qry, Node *node, Oid matviewOid,
+ Relids *relids, bool ex_lock)
+{
+ if (node == NULL)
+ return;
+
+ /* This can recurse, so check for excessive recursion */
+ check_stack_depth();
+
+ switch (nodeTag(node))
+ {
+ case T_Query:
+ {
+ Query *query = (Query *) node;
+
+ CreateIvmTriggersOnBaseTablesRecurse(qry, (Node *)query->jointree, matviewOid, relids, ex_lock);
+ }
+ break;
+
+ case T_RangeTblRef:
+ {
+ int rti = ((RangeTblRef *) node)->rtindex;
+ RangeTblEntry *rte = rt_fetch(rti, qry->rtable);
+
+ if (rte->rtekind == RTE_RELATION && !bms_is_member(rte->relid, *relids))
+ {
+ CreateIvmTrigger(rte->relid, matviewOid, TRIGGER_TYPE_INSERT, TRIGGER_TYPE_BEFORE, ex_lock);
+ CreateIvmTrigger(rte->relid, matviewOid, TRIGGER_TYPE_DELETE, TRIGGER_TYPE_BEFORE, ex_lock);
+ CreateIvmTrigger(rte->relid, matviewOid, TRIGGER_TYPE_UPDATE, TRIGGER_TYPE_BEFORE, ex_lock);
+ CreateIvmTrigger(rte->relid, matviewOid, TRIGGER_TYPE_TRUNCATE, TRIGGER_TYPE_BEFORE, true);
+ CreateIvmTrigger(rte->relid, matviewOid, TRIGGER_TYPE_INSERT, TRIGGER_TYPE_AFTER, ex_lock);
+ CreateIvmTrigger(rte->relid, matviewOid, TRIGGER_TYPE_DELETE, TRIGGER_TYPE_AFTER, ex_lock);
+ CreateIvmTrigger(rte->relid, matviewOid, TRIGGER_TYPE_UPDATE, TRIGGER_TYPE_AFTER, ex_lock);
+ CreateIvmTrigger(rte->relid, matviewOid, TRIGGER_TYPE_TRUNCATE, TRIGGER_TYPE_AFTER, true);
+
+ *relids = bms_add_member(*relids, rte->relid);
+ }
+ }
+ break;
+
+ case T_FromExpr:
+ {
+ FromExpr *f = (FromExpr *) node;
+ ListCell *l;
+
+ foreach(l, f->fromlist)
+ CreateIvmTriggersOnBaseTablesRecurse(qry, lfirst(l), matviewOid, relids, ex_lock);
+ }
+ break;
+
+ case T_JoinExpr:
+ {
+ JoinExpr *j = (JoinExpr *) node;
+
+ CreateIvmTriggersOnBaseTablesRecurse(qry, j->larg, matviewOid, relids, ex_lock);
+ CreateIvmTriggersOnBaseTablesRecurse(qry, j->rarg, matviewOid, relids, ex_lock);
+ }
+ break;
+
+ default:
+ elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
+ }
+}
+
+/*
+ * CreateIvmTrigger -- create IVM trigger on a base table
+ */
+static void
+CreateIvmTrigger(Oid relOid, Oid viewOid, int16 type, int16 timing, bool ex_lock)
+{
+ ObjectAddress refaddr;
+ ObjectAddress address;
+ CreateTrigStmt *ivm_trigger;
+ List *transitionRels = NIL;
+
+ Assert(timing == TRIGGER_TYPE_BEFORE || timing == TRIGGER_TYPE_AFTER);
+
+ refaddr.classId = RelationRelationId;
+ refaddr.objectId = viewOid;
+ refaddr.objectSubId = 0;
+
+ ivm_trigger = makeNode(CreateTrigStmt);
+ ivm_trigger->relation = NULL;
+ ivm_trigger->row = false;
+
+ ivm_trigger->timing = timing;
+ ivm_trigger->events = type;
+
+ switch (type)
+ {
+ case TRIGGER_TYPE_INSERT:
+ ivm_trigger->trigname = (timing == TRIGGER_TYPE_BEFORE ? "IVM_trigger_ins_before" : "IVM_trigger_ins_after");
+ break;
+ case TRIGGER_TYPE_DELETE:
+ ivm_trigger->trigname = (timing == TRIGGER_TYPE_BEFORE ? "IVM_trigger_del_before" : "IVM_trigger_del_after");
+ break;
+ case TRIGGER_TYPE_UPDATE:
+ ivm_trigger->trigname = (timing == TRIGGER_TYPE_BEFORE ? "IVM_trigger_upd_before" : "IVM_trigger_upd_after");
+ break;
+ case TRIGGER_TYPE_TRUNCATE:
+ ivm_trigger->trigname = (timing == TRIGGER_TYPE_BEFORE ? "IVM_trigger_truncate_before" : "IVM_trigger_truncate_after");
+ break;
+ default:
+ elog(ERROR, "unsupported trigger type");
+ }
+
+ if (timing == TRIGGER_TYPE_AFTER)
+ {
+ if (type == TRIGGER_TYPE_INSERT || type == TRIGGER_TYPE_UPDATE)
+ {
+ TriggerTransition *n = makeNode(TriggerTransition);
+ n->name = "__ivm_newtable";
+ n->isNew = true;
+ n->isTable = true;
+
+ transitionRels = lappend(transitionRels, n);
+ }
+ if (type == TRIGGER_TYPE_DELETE || type == TRIGGER_TYPE_UPDATE)
+ {
+ TriggerTransition *n = makeNode(TriggerTransition);
+ n->name = "__ivm_oldtable";
+ n->isNew = false;
+ n->isTable = true;
+
+ transitionRels = lappend(transitionRels, n);
+ }
+ }
+
+ /*
+ * XXX: When using DELETE or UPDATE, we must use exclusive lock for now
+ * because apply_old_delta(_with_count) uses ctid to identify the tuple
+ * to be deleted/deleted, but doesn't work in concurrent situations.
+ *
+ * If the view doesn't have aggregate, distinct, or tuple duplicate,
+ * then it would work even in concurrent situations. However, we don't have
+ * any way to guarantee the view has a unique key before opening the IMMV
+ * at the maintenance time because users may drop the unique index.
+ */
+
+ if (type == TRIGGER_TYPE_DELETE || type == TRIGGER_TYPE_UPDATE)
+ ex_lock = true;
+
+ ivm_trigger->funcname =
+ (timing == TRIGGER_TYPE_BEFORE ? SystemFuncName("IVM_immediate_before") : SystemFuncName("IVM_immediate_maintenance"));
+
+ ivm_trigger->columns = NIL;
+ ivm_trigger->transitionRels = transitionRels;
+ ivm_trigger->whenClause = NULL;
+ ivm_trigger->isconstraint = false;
+ ivm_trigger->deferrable = false;
+ ivm_trigger->initdeferred = false;
+ ivm_trigger->constrrel = NULL;
+ ivm_trigger->args = list_make2(
+ makeString(DatumGetPointer(DirectFunctionCall1(oidout, ObjectIdGetDatum(viewOid)))),
+ makeString(DatumGetPointer(DirectFunctionCall1(boolout, BoolGetDatum(ex_lock))))
+ );
+
+ address = CreateTrigger(ivm_trigger, NULL, relOid, InvalidOid, InvalidOid,
+ InvalidOid, InvalidOid, InvalidOid, NULL, true, false);
+
+ recordDependencyOn(&address, &refaddr, DEPENDENCY_AUTO);
+
+ /* Make changes-so-far visible */
+ CommandCounterIncrement();
+}
+
+/*
+ * check_ivm_restriction --- look for specify nodes in the query tree
+ */
+static void
+check_ivm_restriction(Node *node)
+{
+ check_ivm_restriction_walker(node, NULL);
+}
+
+static bool
+check_ivm_restriction_walker(Node *node, void *context)
+{
+ if (node == NULL)
+ return false;
+
+ /*
+ * We currently don't support Sub-Query.
+ */
+ if (IsA(node, SubPlan) || IsA(node, SubLink))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("subquery is not supported on incrementally maintainable materialized view")));
+
+ /* This can recurse, so check for excessive recursion */
+ check_stack_depth();
+
+ switch (nodeTag(node))
+ {
+ case T_Query:
+ {
+ Query *qry = (Query *)node;
+ ListCell *lc;
+ List *vars;
+
+ /* if contained CTE, return error */
+ if (qry->cteList != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("CTE is not supported on incrementally maintainable materialized view")));
+ if (qry->havingQual != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg(" HAVING clause is not supported on incrementally maintainable materialized view")));
+ if (qry->sortClause != NIL) /* There is a possibility that we don't need to return an error */
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("ORDER BY clause is not supported on incrementally maintainable materialized view")));
+ if (qry->limitOffset != NULL || qry->limitCount != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("LIMIT/OFFSET clause is not supported on incrementally maintainable materialized view")));
+ if (qry->distinctClause)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("DISTINCT is not supported on incrementally maintainable materialized view")));
+ if (qry->hasDistinctOn)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("DISTINCT ON is not supported on incrementally maintainable materialized view")));
+ if (qry->hasWindowFuncs)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("window functions are not supported on incrementally maintainable materialized view")));
+ if (qry->groupingSets != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("GROUPING SETS, ROLLUP, or CUBE clauses is not supported on incrementally maintainable materialized view")));
+ if (qry->setOperations != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("UNION/INTERSECT/EXCEPT statements are not supported on incrementally maintainable materialized view")));
+ if (list_length(qry->targetList) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("empty target list is not supported on incrementally maintainable materialized view")));
+ if (qry->rowMarks != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("FOR UPDATE/SHARE clause is not supported on incrementally maintainable materialized view")));
+
+ /* system column restrictions */
+ vars = pull_vars_of_level((Node *) qry, 0);
+ foreach(lc, vars)
+ {
+ if (IsA(lfirst(lc), Var))
+ {
+ Var *var = (Var *) lfirst(lc);
+ /* if system column, return error */
+ if (var->varattno < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("system column is not supported on incrementally maintainable materialized view")));
+ }
+ }
+
+ /* restrictions for rtable */
+ foreach(lc, qry->rtable)
+ {
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
+
+ if (rte->subquery)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("subquery is not supported on incrementally maintainable materialized view")));
+
+ if (rte->tablesample != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("TABLESAMPLE clause is not supported on incrementally maintainable materialized view")));
+
+ if (rte->relkind == RELKIND_PARTITIONED_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("partitioned table is not supported on incrementally maintainable materialized view")));
+
+ if (rte->relkind == RELKIND_RELATION && has_superclass(rte->relid))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("partitions is not supported on incrementally maintainable materialized view")));
+
+ if (rte->relkind == RELKIND_RELATION && find_inheritance_children(rte->relid, NoLock) != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("inheritance parent is not supported on incrementally maintainable materialized view")));
+
+ if (rte->relkind == RELKIND_FOREIGN_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("foreign table is not supported on incrementally maintainable materialized view")));
+
+ if (rte->relkind == RELKIND_VIEW ||
+ rte->relkind == RELKIND_MATVIEW)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("VIEW or MATERIALIZED VIEW is not supported on incrementally maintainable materialized view")));
+
+ if (rte->rtekind == RTE_VALUES)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("VALUES is not supported on incrementally maintainable materialized view")));
+
+ }
+
+ query_tree_walker(qry, check_ivm_restriction_walker, NULL, QTW_IGNORE_RANGE_TABLE);
+
+ break;
+ }
+ case T_TargetEntry:
+ {
+ TargetEntry *tle = (TargetEntry *)node;
+ if (isIvmName(tle->resname))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("column name %s is not supported on incrementally maintainable materialized view", tle->resname)));
+
+ expression_tree_walker(node, check_ivm_restriction_walker, NULL);
+ break;
+ }
+ case T_JoinExpr:
+ {
+ JoinExpr *joinexpr = (JoinExpr *)node;
+
+ if (joinexpr->jointype > JOIN_INNER)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("OUTER JOIN is not supported on incrementally maintainable materialized view")));
+
+ expression_tree_walker(node, check_ivm_restriction_walker, NULL);
+ }
+ break;
+ case T_Aggref:
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("aggregate function is not supported on incrementally maintainable materialized view")));
+ break;
+ default:
+ expression_tree_walker(node, check_ivm_restriction_walker, (void *) context);
+ break;
+ }
+ return false;
+}
+
+/*
+ * CreateIndexOnIMMV
+ *
+ * Create a unique index on incremental maintainable materialized view.
+ * If the view definition query has a GROUP BY clause, the index is created
+ * on the columns of GROUP BY expressions. Otherwise, if the view contains
+ * all primary key attritubes of its base tables in the target list, the index
+ * is created on these attritubes. In other cases, no index is created.
+ */
+void
+CreateIndexOnIMMV(Query *query, Relation matviewRel)
+{
+ ListCell *lc;
+ IndexStmt *index;
+ ObjectAddress address;
+ List *constraintList = NIL;
+ char idxname[NAMEDATALEN];
+ List *indexoidlist = RelationGetIndexList(matviewRel);
+ ListCell *indexoidscan;
+ Bitmapset *key_attnos;
+
+ snprintf(idxname, sizeof(idxname), "%s_index", RelationGetRelationName(matviewRel));
+
+ index = makeNode(IndexStmt);
+
+ index->unique = true;
+ index->primary = false;
+ index->isconstraint = false;
+ index->deferrable = false;
+ index->initdeferred = false;
+ index->idxname = idxname;
+ index->relation =
+ makeRangeVar(get_namespace_name(RelationGetNamespace(matviewRel)),
+ pstrdup(RelationGetRelationName(matviewRel)),
+ -1);
+ index->accessMethod = DEFAULT_INDEX_TYPE;
+ index->options = NIL;
+ index->tableSpace = get_tablespace_name(matviewRel->rd_rel->reltablespace);
+ index->whereClause = NULL;
+ index->indexParams = NIL;
+ index->indexIncludingParams = NIL;
+ index->excludeOpNames = NIL;
+ index->idxcomment = NULL;
+ index->indexOid = InvalidOid;
+ index->oldNumber = InvalidRelFileNumber;
+ index->oldCreateSubid = InvalidSubTransactionId;
+ index->oldFirstRelfilelocatorSubid = InvalidSubTransactionId;
+ index->transformed = true;
+ index->concurrent = false;
+ index->if_not_exists = false;
+
+ /* create index on the base tables' primary key columns */
+ key_attnos = get_primary_key_attnos_from_query(query, &constraintList);
+ if (key_attnos)
+ {
+ foreach(lc, query->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+ Form_pg_attribute attr = TupleDescAttr(matviewRel->rd_att, tle->resno - 1);
+
+ if (bms_is_member(tle->resno - FirstLowInvalidHeapAttributeNumber, key_attnos))
+ {
+ IndexElem *iparam;
+
+ iparam = makeNode(IndexElem);
+ iparam->name = pstrdup(NameStr(attr->attname));
+ iparam->expr = NULL;
+ iparam->indexcolname = NULL;
+ iparam->collation = NIL;
+ iparam->opclass = NIL;
+ iparam->opclassopts = NIL;
+ iparam->ordering = SORTBY_DEFAULT;
+ iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
+ index->indexParams = lappend(index->indexParams, iparam);
+ }
+ }
+ }
+ else
+ {
+ /* create no index, just notice that an appropriate index is necessary for efficient IVM */
+ ereport(NOTICE,
+ (errmsg("could not create an index on materialized view \"%s\" automatically",
+ RelationGetRelationName(matviewRel)),
+ errdetail("This target list does not have all the primary key columns. "),
+ errhint("Create an index on the materialized view for efficient incremental maintenance.")));
+ return;
+ }
+
+ /* If we have a compatible index, we don't need to create another. */
+ foreach(indexoidscan, indexoidlist)
+ {
+ Oid indexoid = lfirst_oid(indexoidscan);
+ Relation indexRel;
+ bool hasCompatibleIndex = false;
+
+ indexRel = index_open(indexoid, AccessShareLock);
+
+ if (CheckIndexCompatible(indexRel->rd_id,
+ index->accessMethod,
+ index->indexParams,
+ index->excludeOpNames,
+ false))
+ hasCompatibleIndex = true;
+
+ index_close(indexRel, AccessShareLock);
+
+ if (hasCompatibleIndex)
+ return;
+ }
+
+ address = DefineIndex(RelationGetRelid(matviewRel),
+ index,
+ InvalidOid,
+ InvalidOid,
+ InvalidOid,
+ -1,
+ false, true, false, false, true);
+
+ ereport(NOTICE,
+ (errmsg("created index \"%s\" on materialized view \"%s\"",
+ idxname, RelationGetRelationName(matviewRel))));
+
+ /*
+ * Make dependencies so that the index is dropped if any base tables's
+ * primary key is dropped.
+ */
+ foreach(lc, constraintList)
+ {
+ Oid constraintOid = lfirst_oid(lc);
+ ObjectAddress refaddr;
+
+ refaddr.classId = ConstraintRelationId;
+ refaddr.objectId = constraintOid;
+ refaddr.objectSubId = 0;
+
+ recordDependencyOn(&address, &refaddr, DEPENDENCY_NORMAL);
+ }
+}
+
+
+/*
+ * get_primary_key_attnos_from_query
+ *
+ * Identify the columns in base tables' primary keys in the target list.
+ *
+ * Returns a Bitmapset of the column attnos of the primary key's columns of
+ * tables that used in the query. The attnos are offset by
+ * FirstLowInvalidHeapAttributeNumber as same as get_primary_key_attnos.
+ *
+ * If any table has no primary key or any primary key's columns is not in
+ * the target list, return NULL. We also return NULL if any pkey constraint
+ * is deferrable.
+ *
+ * constraintList is set to a list of the OIDs of the pkey constraints.
+ */
+static Bitmapset *
+get_primary_key_attnos_from_query(Query *query, List **constraintList)
+{
+ List *key_attnos_list = NIL;
+ ListCell *lc;
+ int i;
+ Bitmapset *keys = NULL;
+ Relids rels_in_from;
+
+ /*
+ * Collect primary key attributes from all tables used in query. The key attributes
+ * sets for each table are stored in key_attnos_list in order by RTE index.
+ */
+ foreach(lc, query->rtable)
+ {
+ RangeTblEntry *r = (RangeTblEntry*) lfirst(lc);
+ Bitmapset *key_attnos;
+ bool has_pkey = true;
+
+ /* for tables, call get_primary_key_attnos */
+ if (r->rtekind == RTE_RELATION)
+ {
+ Oid constraintOid;
+ key_attnos = get_primary_key_attnos(r->relid, false, &constraintOid);
+ *constraintList = lappend_oid(*constraintList, constraintOid);
+ has_pkey = (key_attnos != NULL);
+ }
+ /* for other RTEs, store NULL into key_attnos_list */
+ else
+ key_attnos = NULL;
+
+ /*
+ * If any table or subquery has no primary key or its pkey constraint is deferrable,
+ * we cannot get key attributes for this query, so return NULL.
+ */
+ if (!has_pkey)
+ return NULL;
+
+ key_attnos_list = lappend(key_attnos_list, key_attnos);
+ }
+
+ /* Collect key attributes appearing in the target list */
+ i = 1;
+ foreach(lc, query->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) flatten_join_alias_vars(NULL, query, lfirst(lc));
+
+ if (IsA(tle->expr, Var))
+ {
+ Var *var = (Var*) tle->expr;
+ Bitmapset *key_attnos = list_nth(key_attnos_list, var->varno - 1);
+
+ /* check if this attribute is from a base table's primary key */
+ if (bms_is_member(var->varattno - FirstLowInvalidHeapAttributeNumber, key_attnos))
+ {
+ /*
+ * Remove found key attributes from key_attnos_list, and add this
+ * to the result list.
+ */
+ key_attnos = bms_del_member(key_attnos, var->varattno - FirstLowInvalidHeapAttributeNumber);
+ if (bms_is_empty(key_attnos))
+ {
+ key_attnos_list = list_delete_nth_cell(key_attnos_list, var->varno - 1);
+ key_attnos_list = list_insert_nth(key_attnos_list, var->varno - 1, NULL);
+ }
+ keys = bms_add_member(keys, i - FirstLowInvalidHeapAttributeNumber);
+ }
+ }
+ i++;
+ }
+
+ /* Collect RTE indexes of relations appearing in the FROM clause */
+ rels_in_from = get_relids_in_jointree((Node *) query->jointree, false, false);
+
+ /*
+ * Check if all key attributes of relations in FROM are appearing in the target
+ * list. If an attribute remains in key_attnos_list in spite of the table is used
+ * in FROM clause, the target is missing this key attribute, so we return NULL.
+ */
+ i = 1;
+ foreach(lc, key_attnos_list)
+ {
+ Bitmapset *bms = (Bitmapset *)lfirst(lc);
+ if (!bms_is_empty(bms) && bms_is_member(i, rels_in_from))
+ return NULL;
+ i++;
+ }
+
+ return keys;
+}
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 59920ced83..a821992a37 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -25,26 +25,37 @@
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_am.h"
+#include "catalog/pg_depend.h"
+#include "catalog/pg_trigger.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "commands/cluster.h"
#include "commands/matview.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
+#include "commands/createas.h"
#include "executor/executor.h"
#include "executor/spi.h"
+#include "executor/tstoreReceiver.h"
#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "parser/analyze.h"
+#include "parser/parse_clause.h"
+#include "parser/parse_func.h"
#include "parser/parse_relation.h"
#include "pgstat.h"
#include "rewrite/rewriteHandler.h"
+#include "rewrite/rowsecurity.h"
#include "storage/lmgr.h"
#include "storage/smgr.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
+#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
+#include "utils/typcache.h"
typedef struct
@@ -58,6 +69,52 @@ typedef struct
BulkInsertState bistate; /* bulk insert state */
} DR_transientrel;
+#define MV_INIT_QUERYHASHSIZE 16
+
+/*
+ * MV_TriggerHashEntry
+ *
+ * Hash entry for base tables on which IVM trigger is invoked
+ */
+typedef struct MV_TriggerHashEntry
+{
+ Oid matview_id; /* OID of the materialized view */
+ int before_trig_count; /* count of before triggers invoked */
+ int after_trig_count; /* count of after triggers invoked */
+
+ Snapshot snapshot; /* Snapshot just before table change */
+
+ List *tables; /* List of MV_TriggerTable */
+ bool has_old; /* tuples are deleted from any table? */
+ bool has_new; /* tuples are inserted into any table? */
+} MV_TriggerHashEntry;
+
+/*
+ * MV_TriggerTable
+ *
+ * IVM related data for tables on which the trigger is invoked.
+ */
+typedef struct MV_TriggerTable
+{
+ Oid table_id; /* OID of the modified table */
+ List *old_tuplestores; /* tuplestores for deleted tuples */
+ List *new_tuplestores; /* tuplestores for inserted tuples */
+
+ List *rte_indexes; /* List of RTE index of the modified table */
+ RangeTblEntry *original_rte; /* the original RTE saved before rewriting query */
+
+ Relation rel; /* relation of the modified table */
+ TupleTableSlot *slot; /* for checking visibility in the pre-state table */
+} MV_TriggerTable;
+
+static HTAB *mv_trigger_info = NULL;
+
+static bool in_delta_calculation = false;
+
+/* ENR name for materialized view delta */
+#define NEW_DELTA_ENRNAME "new_delta"
+#define OLD_DELTA_ENRNAME "old_delta"
+
static int matview_maintenance_depth = 0;
static void transientrel_startup(DestReceiver *self, int operation, TupleDesc typeinfo);
@@ -65,7 +122,9 @@ static bool transientrel_receive(TupleTableSlot *slot, DestReceiver *self);
static void transientrel_shutdown(DestReceiver *self);
static void transientrel_destroy(DestReceiver *self);
static uint64 refresh_matview_datafill(DestReceiver *dest, Query *query,
- const char *queryString);
+ QueryEnvironment *queryEnv,
+ TupleDesc *resultTupleDesc,
+ const char *queryString);
static char *make_temptable_name_n(char *tempname, int n);
static void refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
int save_sec_context);
@@ -73,6 +132,37 @@ static void refresh_by_heap_swap(Oid matviewOid, Oid OIDNewHeap, char relpersist
static bool is_usable_unique_index(Relation indexRel);
static void OpenMatViewIncrementalMaintenance(void);
static void CloseMatViewIncrementalMaintenance(void);
+static Query *get_matview_query(Relation matviewRel);
+
+static Query *rewrite_query_for_preupdate_state(Query *query, List *tables,
+ ParseState *pstate, Oid matviewid);
+static void register_delta_ENRs(ParseState *pstate, Query *query, List *tables);
+static char *make_delta_enr_name(const char *prefix, Oid relid, int count);
+static RangeTblEntry *get_prestate_rte(RangeTblEntry *rte, MV_TriggerTable *table,
+ QueryEnvironment *queryEnv, Oid matviewid);
+static RangeTblEntry *replace_rte_with_delta(RangeTblEntry *rte, MV_TriggerTable *table, bool is_new,
+ QueryEnvironment *queryEnv);
+static Query *rewrite_query_for_counting(Query *query, ParseState *pstate);
+
+static void calc_delta(MV_TriggerTable *table, int rte_index, Query *query,
+ DestReceiver *dest_old, DestReceiver *dest_new,
+ TupleDesc *tupdesc_old, TupleDesc *tupdesc_new,
+ QueryEnvironment *queryEnv);
+static Query *rewrite_query_for_postupdate_state(Query *query, MV_TriggerTable *table, int rte_index);
+
+static void apply_delta(Oid matviewOid, Tuplestorestate *old_tuplestores, Tuplestorestate *new_tuplestores,
+ TupleDesc tupdesc_old, TupleDesc tupdesc_new,
+ Query *query);
+static void apply_old_delta(const char *matviewname, const char *deltaname_old,
+ List *keys);
+static void apply_new_delta(const char *matviewname, const char *deltaname_new,
+ StringInfo target_list);
+static char *get_matching_condition_string(List *keys);
+static void generate_equal(StringInfo querybuf, Oid opttype,
+ const char *leftop, const char *rightop);
+
+static void mv_InitHashTables(void);
+static void clean_up_IVM_hash_entry(MV_TriggerHashEntry *entry, bool is_abort);
/*
* SetMatViewPopulatedState
@@ -114,6 +204,46 @@ SetMatViewPopulatedState(Relation relation, bool newstate)
CommandCounterIncrement();
}
+/*
+ * SetMatViewIVMState
+ * Mark a materialized view as IVM, or not.
+ *
+ * NOTE: caller must be holding an appropriate lock on the relation.
+ */
+void
+SetMatViewIVMState(Relation relation, bool newstate)
+{
+ Relation pgrel;
+ HeapTuple tuple;
+
+ Assert(relation->rd_rel->relkind == RELKIND_MATVIEW);
+
+ /*
+ * Update relation's pg_class entry. Crucial side-effect: other backends
+ * (and this one too!) are sent SI message to make them rebuild relcache
+ * entries.
+ */
+ pgrel = table_open(RelationRelationId, RowExclusiveLock);
+ tuple = SearchSysCacheCopy1(RELOID,
+ ObjectIdGetDatum(RelationGetRelid(relation)));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u",
+ RelationGetRelid(relation));
+
+ ((Form_pg_class) GETSTRUCT(tuple))->relisivm = newstate;
+
+ CatalogTupleUpdate(pgrel, &tuple->t_self, tuple);
+
+ heap_freetuple(tuple);
+ table_close(pgrel, RowExclusiveLock);
+
+ /*
+ * Advance command counter to make the updated pg_class row locally
+ * visible.
+ */
+ CommandCounterIncrement();
+}
+
/*
* ExecRefreshMatView -- execute a REFRESH MATERIALIZED VIEW command
*
@@ -140,8 +270,6 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
{
Oid matviewOid;
Relation matviewRel;
- RewriteRule *rule;
- List *actions;
Query *dataQuery;
Oid tableSpace;
Oid relowner;
@@ -155,6 +283,7 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
int save_sec_context;
int save_nestlevel;
ObjectAddress address;
+ bool oldPopulated;
/* Determine strength of lock needed. */
concurrent = stmt->concurrent;
@@ -178,6 +307,7 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
SetUserIdAndSecContext(relowner,
save_sec_context | SECURITY_RESTRICTED_OPERATION);
save_nestlevel = NewGUCNestLevel();
+ oldPopulated = RelationIsPopulated(matviewRel);
/* Make sure it is a materialized view. */
if (matviewRel->rd_rel->relkind != RELKIND_MATVIEW)
@@ -199,32 +329,9 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
errmsg("%s and %s options cannot be used together",
"CONCURRENTLY", "WITH NO DATA")));
- /*
- * Check that everything is correct for a refresh. Problems at this point
- * are internal errors, so elog is sufficient.
- */
- if (matviewRel->rd_rel->relhasrules == false ||
- matviewRel->rd_rules->numLocks < 1)
- elog(ERROR,
- "materialized view \"%s\" is missing rewrite information",
- RelationGetRelationName(matviewRel));
-
- if (matviewRel->rd_rules->numLocks > 1)
- elog(ERROR,
- "materialized view \"%s\" has too many rules",
- RelationGetRelationName(matviewRel));
- rule = matviewRel->rd_rules->rules[0];
- if (rule->event != CMD_SELECT || !(rule->isInstead))
- elog(ERROR,
- "the rule for materialized view \"%s\" is not a SELECT INSTEAD OF rule",
- RelationGetRelationName(matviewRel));
+ dataQuery = get_matview_query(matviewRel);
- actions = rule->actions;
- if (list_length(actions) != 1)
- elog(ERROR,
- "the rule for materialized view \"%s\" is not a single action",
- RelationGetRelationName(matviewRel));
/*
* Check that there is a unique index with no WHERE clause on one or more
@@ -259,12 +366,6 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
errhint("Create a unique index with no WHERE clause on one or more columns of the materialized view.")));
}
- /*
- * The stored query was rewritten at the time of the MV definition, but
- * has not been scribbled on by the planner.
- */
- dataQuery = linitial_node(Query, actions);
-
/*
* Check for active uses of the relation in the current transaction, such
* as open scans.
@@ -292,6 +393,74 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
relpersistence = matviewRel->rd_rel->relpersistence;
}
+ /* delete IMMV triggers. */
+ if (RelationIsIVM(matviewRel) && stmt->skipData )
+ {
+ Relation tgRel;
+ Relation depRel;
+ ScanKeyData key;
+ SysScanDesc scan;
+ HeapTuple tup;
+ ObjectAddresses *immv_triggers;
+
+ immv_triggers = new_object_addresses();
+
+ tgRel = table_open(TriggerRelationId, RowExclusiveLock);
+ depRel = table_open(DependRelationId, RowExclusiveLock);
+
+ /* search triggers that depends on IMMV. */
+ ScanKeyInit(&key,
+ Anum_pg_depend_refobjid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(matviewOid));
+ scan = systable_beginscan(depRel, DependReferenceIndexId, true,
+ NULL, 1, &key);
+ while ((tup = systable_getnext(scan)) != NULL)
+ {
+ ObjectAddress obj;
+ Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
+
+ if (foundDep->classid == TriggerRelationId)
+ {
+ HeapTuple tgtup;
+ ScanKeyData tgkey[1];
+ SysScanDesc tgscan;
+ Form_pg_trigger tgform;
+
+ /* Find the trigger name. */
+ ScanKeyInit(&tgkey[0],
+ Anum_pg_trigger_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(foundDep->objid));
+
+ tgscan = systable_beginscan(tgRel, TriggerOidIndexId, true,
+ NULL, 1, tgkey);
+ tgtup = systable_getnext(tgscan);
+ if (!HeapTupleIsValid(tgtup))
+ elog(ERROR, "could not find tuple for immv trigger %u", foundDep->objid);
+
+ tgform = (Form_pg_trigger) GETSTRUCT(tgtup);
+
+ /* If trigger is created by IMMV, delete it. */
+ if (strncmp(NameStr(tgform->tgname), "IVM_trigger_", 12) == 0)
+ {
+ obj.classId = foundDep->classid;
+ obj.objectId = foundDep->objid;
+ obj.objectSubId = foundDep->refobjsubid;
+ add_exact_object_address(&obj, immv_triggers);
+ }
+ systable_endscan(tgscan);
+ }
+ }
+ systable_endscan(scan);
+
+ performMultipleDeletions(immv_triggers, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
+
+ table_close(depRel, RowExclusiveLock);
+ table_close(tgRel, RowExclusiveLock);
+ free_object_addresses(immv_triggers);
+ }
+
/*
* Create the transient table that will receive the regenerated data. Lock
* it against access by any other process until commit (by which time it
@@ -305,7 +474,7 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
/* Generate the data, if wanted. */
if (!stmt->skipData)
- processed = refresh_matview_datafill(dest, dataQuery, queryString);
+ processed = refresh_matview_datafill(dest, dataQuery, NULL, NULL, queryString);
/* Make the matview match the newly generated data. */
if (concurrent)
@@ -340,6 +509,12 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
pgstat_count_heap_insert(matviewRel, processed);
}
+ if (!stmt->skipData && RelationIsIVM(matviewRel) && !oldPopulated)
+ {
+ CreateIndexOnIMMV(dataQuery, matviewRel);
+ CreateIvmTriggersOnBaseTables(dataQuery, matviewOid);
+ }
+
table_close(matviewRel, NoLock);
/* Roll back any GUC changes */
@@ -374,6 +549,8 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
*/
static uint64
refresh_matview_datafill(DestReceiver *dest, Query *query,
+ QueryEnvironment *queryEnv,
+ TupleDesc *resultTupleDesc,
const char *queryString)
{
List *rewritten;
@@ -410,7 +587,7 @@ refresh_matview_datafill(DestReceiver *dest, Query *query,
/* Create a QueryDesc, redirecting output to our tuple receiver */
queryDesc = CreateQueryDesc(plan, queryString,
GetActiveSnapshot(), InvalidSnapshot,
- dest, NULL, NULL, 0);
+ dest, NULL, queryEnv ? queryEnv: NULL, 0);
/* call ExecutorStart to prepare the plan for execution */
ExecutorStart(queryDesc, 0);
@@ -420,6 +597,9 @@ refresh_matview_datafill(DestReceiver *dest, Query *query,
processed = queryDesc->estate->es_processed;
+ if (resultTupleDesc)
+ *resultTupleDesc = CreateTupleDescCopy(queryDesc->tupDesc);
+
/* and clean up */
ExecutorFinish(queryDesc);
ExecutorEnd(queryDesc);
@@ -953,3 +1133,1219 @@ CloseMatViewIncrementalMaintenance(void)
matview_maintenance_depth--;
Assert(matview_maintenance_depth >= 0);
}
+
+/*
+ * get_matview_query - get the Query from a matview's _RETURN rule.
+ */
+static Query *
+get_matview_query(Relation matviewRel)
+{
+ RewriteRule *rule;
+ List * actions;
+
+ /*
+ * Check that everything is correct for a refresh. Problems at this point
+ * are internal errors, so elog is sufficient.
+ */
+ if (matviewRel->rd_rel->relhasrules == false ||
+ matviewRel->rd_rules->numLocks < 1)
+ elog(ERROR,
+ "materialized view \"%s\" is missing rewrite information",
+ RelationGetRelationName(matviewRel));
+
+ if (matviewRel->rd_rules->numLocks > 1)
+ elog(ERROR,
+ "materialized view \"%s\" has too many rules",
+ RelationGetRelationName(matviewRel));
+
+ rule = matviewRel->rd_rules->rules[0];
+ if (rule->event != CMD_SELECT || !(rule->isInstead))
+ elog(ERROR,
+ "the rule for materialized view \"%s\" is not a SELECT INSTEAD OF rule",
+ RelationGetRelationName(matviewRel));
+
+ actions = rule->actions;
+ if (list_length(actions) != 1)
+ elog(ERROR,
+ "the rule for materialized view \"%s\" is not a single action",
+ RelationGetRelationName(matviewRel));
+
+ /*
+ * The stored query was rewritten at the time of the MV definition, but
+ * has not been scribbled on by the planner.
+ */
+ return linitial_node(Query, actions);
+}
+
+
+/* ----------------------------------------------------
+ * Incremental View Maintenance routines
+ * ---------------------------------------------------
+ */
+
+/*
+ * IVM_immediate_before
+ *
+ * IVM trigger function invoked before base table is modified. If this is
+ * invoked firstly in the same statement, we save the transaction id and the
+ * command id at that time.
+ */
+Datum
+IVM_immediate_before(PG_FUNCTION_ARGS)
+{
+ TriggerData *trigdata = (TriggerData *) fcinfo->context;
+ char *matviewOid_text = trigdata->tg_trigger->tgargs[0];
+ char *ex_lock_text = trigdata->tg_trigger->tgargs[1];
+ Oid matviewOid;
+ MV_TriggerHashEntry *entry;
+ bool found;
+ bool ex_lock;
+
+ matviewOid = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(matviewOid_text)));
+ ex_lock = DatumGetBool(DirectFunctionCall1(boolin, CStringGetDatum(ex_lock_text)));
+
+ /* If the view has more than one tables, we have to use an exclusive lock. */
+ if (ex_lock)
+ {
+ /*
+ * Wait for concurrent transactions which update this materialized view at
+ * READ COMMITED. This is needed to see changes committed in other
+ * transactions. No wait and raise an error at REPEATABLE READ or
+ * SERIALIZABLE to prevent update anomalies of matviews.
+ * XXX: dead-lock is possible here.
+ */
+ if (!IsolationUsesXactSnapshot())
+ LockRelationOid(matviewOid, ExclusiveLock);
+ else if (!ConditionalLockRelationOid(matviewOid, ExclusiveLock))
+ {
+ /* try to throw error by name; relation could be deleted... */
+ char *relname = get_rel_name(matviewOid);
+
+ if (!relname)
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on materialized view during incremental maintenance")));
+
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on materialized view \"%s\" during incremental maintenance",
+ relname)));
+ }
+ }
+ else
+ LockRelationOid(matviewOid, RowExclusiveLock);
+
+ /*
+ * On the first call initialize the hashtable
+ */
+ if (!mv_trigger_info)
+ mv_InitHashTables();
+
+ entry = (MV_TriggerHashEntry *) hash_search(mv_trigger_info,
+ (void *) &matviewOid,
+ HASH_ENTER, &found);
+
+ /* On the first BEFORE to update the view, initialize trigger data */
+ if (!found)
+ {
+ /*
+ * Get a snapshot just before the table was modified for checking
+ * tuple visibility in the pre-update state of the table.
+ */
+ Snapshot snapshot = GetActiveSnapshot();
+
+ entry->matview_id = matviewOid;
+ entry->before_trig_count = 0;
+ entry->after_trig_count = 0;
+ entry->snapshot = RegisterSnapshot(snapshot);
+ entry->tables = NIL;
+ entry->has_old = false;
+ entry->has_new = false;
+ }
+
+ entry->before_trig_count++;
+
+ return PointerGetDatum(NULL);
+}
+
+/*
+ * IVM_immediate_maintenance
+ *
+ * IVM trigger function invoked after base table is modified.
+ * For each table, tuplestores of transition tables are collected.
+ * and after the last modification
+ */
+Datum
+IVM_immediate_maintenance(PG_FUNCTION_ARGS)
+{
+ TriggerData *trigdata = (TriggerData *) fcinfo->context;
+ Relation rel;
+ Oid relid;
+ Oid matviewOid;
+ Query *query;
+ Query *rewritten = NULL;
+ char *matviewOid_text = trigdata->tg_trigger->tgargs[0];
+ Relation matviewRel;
+ int old_depth = matview_maintenance_depth;
+
+ Oid relowner;
+ Tuplestorestate *old_tuplestore = NULL;
+ Tuplestorestate *new_tuplestore = NULL;
+ DestReceiver *dest_new = NULL, *dest_old = NULL;
+ Oid save_userid;
+ int save_sec_context;
+ int save_nestlevel;
+
+ MV_TriggerHashEntry *entry;
+ MV_TriggerTable *table;
+ bool found;
+
+ ParseState *pstate;
+ QueryEnvironment *queryEnv = create_queryEnv();
+ MemoryContext oldcxt;
+ ListCell *lc;
+ int i;
+
+
+ /* Create a ParseState for rewriting the view definition query */
+ pstate = make_parsestate(NULL);
+ pstate->p_queryEnv = queryEnv;
+ pstate->p_expr_kind = EXPR_KIND_SELECT_TARGET;
+
+ rel = trigdata->tg_relation;
+ relid = rel->rd_id;
+
+ matviewOid = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(matviewOid_text)));
+
+ /*
+ * On the first call initialize the hashtable
+ */
+ if (!mv_trigger_info)
+ mv_InitHashTables();
+
+ /* get the entry for this materialized view */
+ entry = (MV_TriggerHashEntry *) hash_search(mv_trigger_info,
+ (void *) &matviewOid,
+ HASH_FIND, &found);
+ Assert (found && entry != NULL);
+ entry->after_trig_count++;
+
+ /* search the entry for the modified table and create new entry if not found */
+ found = false;
+ foreach(lc, entry->tables)
+ {
+ table = (MV_TriggerTable *) lfirst(lc);
+ if (table->table_id == relid)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ oldcxt = MemoryContextSwitchTo(TopTransactionContext);
+
+ table = (MV_TriggerTable *) palloc0(sizeof(MV_TriggerTable));
+ table->table_id = relid;
+ table->old_tuplestores = NIL;
+ table->new_tuplestores = NIL;
+ table->rte_indexes = NIL;
+ table->slot = MakeSingleTupleTableSlot(RelationGetDescr(rel), table_slot_callbacks(rel));
+ table->rel = table_open(RelationGetRelid(rel), NoLock);
+ entry->tables = lappend(entry->tables, table);
+
+ MemoryContextSwitchTo(oldcxt);
+ }
+
+ /* Save the transition tables and make a request to not free immediately */
+ if (trigdata->tg_oldtable)
+ {
+ oldcxt = MemoryContextSwitchTo(TopTransactionContext);
+ table->old_tuplestores = lappend(table->old_tuplestores, trigdata->tg_oldtable);
+ entry->has_old = true;
+ MemoryContextSwitchTo(oldcxt);
+ }
+ if (trigdata->tg_newtable)
+ {
+ oldcxt = MemoryContextSwitchTo(TopTransactionContext);
+ table->new_tuplestores = lappend(table->new_tuplestores, trigdata->tg_newtable);
+ entry->has_new = true;
+ MemoryContextSwitchTo(oldcxt);
+ }
+ if (entry->has_new || entry->has_old)
+ {
+ CmdType cmd;
+
+ if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
+ cmd = CMD_INSERT;
+ else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
+ cmd = CMD_DELETE;
+ else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
+ cmd = CMD_UPDATE;
+ else
+ elog(ERROR,"unsupported trigger type");
+
+ /* Prolong lifespan of transition tables to the end of the last AFTER trigger */
+ SetTransitionTablePreserved(relid, cmd);
+ }
+
+
+ /* If this is not the last AFTER trigger call, immediately exit. */
+ Assert (entry->before_trig_count >= entry->after_trig_count);
+ if (entry->before_trig_count != entry->after_trig_count)
+ return PointerGetDatum(NULL);
+
+ /*
+ * If this is the last AFTER trigger call, continue and update the view.
+ */
+
+ /*
+ * Advance command counter to make the updated base table row locally
+ * visible.
+ */
+ CommandCounterIncrement();
+
+ matviewRel = table_open(matviewOid, NoLock);
+
+ /* Make sure it is a materialized view. */
+ Assert(matviewRel->rd_rel->relkind == RELKIND_MATVIEW);
+
+ /*
+ * Get and push the latast snapshot to see any changes which is committed
+ * during waiting in other transactions at READ COMMITTED level.
+ */
+ PushActiveSnapshot(GetTransactionSnapshot());
+
+ /*
+ * Check for active uses of the relation in the current transaction, such
+ * as open scans.
+ *
+ * NB: We count on this to protect us against problems with refreshing the
+ * data using TABLE_INSERT_FROZEN.
+ */
+ CheckTableNotInUse(matviewRel, "refresh a materialized view incrementally");
+
+ /*
+ * Switch to the owner's userid, so that any functions are run as that
+ * user. Also arrange to make GUC variable changes local to this command.
+ * We will switch modes when we are about to execute user code.
+ */
+ relowner = matviewRel->rd_rel->relowner;
+ GetUserIdAndSecContext(&save_userid, &save_sec_context);
+ SetUserIdAndSecContext(relowner,
+ save_sec_context | SECURITY_RESTRICTED_OPERATION);
+ save_nestlevel = NewGUCNestLevel();
+
+ /* get view query*/
+ query = get_matview_query(matviewRel);
+
+ /*
+ * When a base table is truncated, the view content will be empty if the
+ * view definition query does not contain an aggregate without a GROUP clause.
+ * Therefore, such views can be truncated.
+ */
+ if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event))
+ {
+ ExecuteTruncateGuts(list_make1(matviewRel), list_make1_oid(matviewOid),
+ NIL, DROP_RESTRICT, false, false);
+
+ /* Clean up hash entry and delete tuplestores */
+ clean_up_IVM_hash_entry(entry, false);
+
+ /* Pop the original snapshot. */
+ PopActiveSnapshot();
+
+ table_close(matviewRel, NoLock);
+
+ /* Roll back any GUC changes */
+ AtEOXact_GUC(false, save_nestlevel);
+
+ /* Restore userid and security context */
+ SetUserIdAndSecContext(save_userid, save_sec_context);
+
+ return PointerGetDatum(NULL);
+ }
+
+ /*
+ * rewrite query for calculating deltas
+ */
+
+ rewritten = copyObject(query);
+
+ /* Replace resnames in a target list with materialized view's attnames */
+ i = 0;
+ foreach (lc, rewritten->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+ Form_pg_attribute attr = TupleDescAttr(matviewRel->rd_att, i);
+ char *resname = NameStr(attr->attname);
+
+ tle->resname = pstrdup(resname);
+ i++;
+ }
+
+ /* Set all tables in the query to pre-update state */
+ rewritten = rewrite_query_for_preupdate_state(rewritten, entry->tables,
+ pstate, matviewOid);
+ /* Rewrite for counting duplicated tuples */
+ rewritten = rewrite_query_for_counting(rewritten, pstate);
+
+ /* Create tuplestores to store view deltas */
+ if (entry->has_old)
+ {
+ oldcxt = MemoryContextSwitchTo(TopTransactionContext);
+
+ old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
+ dest_old = CreateDestReceiver(DestTuplestore);
+ SetTuplestoreDestReceiverParams(dest_old,
+ old_tuplestore,
+ TopTransactionContext,
+ false,
+ NULL,
+ NULL);
+
+ MemoryContextSwitchTo(oldcxt);
+ }
+ if (entry->has_new)
+ {
+ oldcxt = MemoryContextSwitchTo(TopTransactionContext);
+
+ new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
+ dest_new = CreateDestReceiver(DestTuplestore);
+ SetTuplestoreDestReceiverParams(dest_new,
+ new_tuplestore,
+ TopTransactionContext,
+ false,
+ NULL,
+ NULL);
+ MemoryContextSwitchTo(oldcxt);
+ }
+
+ /* for all modified tables */
+ foreach(lc, entry->tables)
+ {
+ ListCell *lc2;
+
+ table = (MV_TriggerTable *) lfirst(lc);
+
+ /* loop for self-join */
+ foreach(lc2, table->rte_indexes)
+ {
+ int rte_index = lfirst_int(lc2);
+ TupleDesc tupdesc_old;
+ TupleDesc tupdesc_new;
+
+ /* calculate delta tables */
+ calc_delta(table, rte_index, rewritten, dest_old, dest_new,
+ &tupdesc_old, &tupdesc_new, queryEnv);
+
+ /* Set the table in the query to post-update state */
+ rewritten = rewrite_query_for_postupdate_state(rewritten, table, rte_index);
+
+ PG_TRY();
+ {
+ /* apply the delta tables to the materialized view */
+ apply_delta(matviewOid, old_tuplestore, new_tuplestore,
+ tupdesc_old, tupdesc_new, query);
+ }
+ PG_CATCH();
+ {
+ matview_maintenance_depth = old_depth;
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ /* clear view delta tuplestores */
+ if (old_tuplestore)
+ tuplestore_clear(old_tuplestore);
+ if (new_tuplestore)
+ tuplestore_clear(new_tuplestore);
+ }
+ }
+
+ /* Clean up hash entry and delete tuplestores */
+ clean_up_IVM_hash_entry(entry, false);
+ if (old_tuplestore)
+ {
+ dest_old->rDestroy(dest_old);
+ tuplestore_end(old_tuplestore);
+ }
+ if (new_tuplestore)
+ {
+ dest_new->rDestroy(dest_new);
+ tuplestore_end(new_tuplestore);
+ }
+
+ /* Pop the original snapshot. */
+ PopActiveSnapshot();
+
+ table_close(matviewRel, NoLock);
+
+ /* Roll back any GUC changes */
+ AtEOXact_GUC(false, save_nestlevel);
+
+ /* Restore userid and security context */
+ SetUserIdAndSecContext(save_userid, save_sec_context);
+
+ return PointerGetDatum(NULL);
+}
+
+/*
+ * rewrite_query_for_preupdate_state
+ *
+ * Rewrite the query so that base tables' RTEs will represent "pre-update"
+ * state of tables. This is necessary to calculate view delta after multiple
+ * tables are modified.
+ */
+static Query*
+rewrite_query_for_preupdate_state(Query *query, List *tables,
+ ParseState *pstate, Oid matviewid)
+{
+ ListCell *lc;
+ int num_rte = list_length(query->rtable);
+ int i;
+
+
+ /* register delta ENRs */
+ register_delta_ENRs(pstate, query, tables);
+
+ /* XXX: Is necessary? Is this right timing? */
+ AcquireRewriteLocks(query, true, false);
+
+ i = 1;
+ foreach(lc, query->rtable)
+ {
+ RangeTblEntry *r = (RangeTblEntry*) lfirst(lc);
+
+ ListCell *lc2;
+ foreach(lc2, tables)
+ {
+ MV_TriggerTable *table = (MV_TriggerTable *) lfirst(lc2);
+ /*
+ * if the modified table is found then replace the original RTE with
+ * "pre-state" RTE and append its index to the list.
+ */
+ if (r->relid == table->table_id)
+ {
+ List *securityQuals;
+ List *withCheckOptions;
+ bool hasRowSecurity;
+ bool hasSubLinks;
+
+ RangeTblEntry *rte_pre = get_prestate_rte(r, table, pstate->p_queryEnv, matviewid);
+
+ /*
+ * Set a row security poslicies of the modified table to the subquery RTE which
+ * represents the pre-update state of the table.
+ */
+ get_row_security_policies(query, table->original_rte, i,
+ &securityQuals, &withCheckOptions,
+ &hasRowSecurity, &hasSubLinks);
+
+ if (hasRowSecurity)
+ {
+ query->hasRowSecurity = true;
+ rte_pre->security_barrier = true;
+ }
+ if (hasSubLinks)
+ query->hasSubLinks = true;
+
+ rte_pre->securityQuals = securityQuals;
+ lfirst(lc) = rte_pre;
+
+ table->rte_indexes = lappend_int(table->rte_indexes, i);
+ break;
+ }
+ }
+
+ /* finish the loop if we processed all RTE included in the original query */
+ if (i++ >= num_rte)
+ break;
+ }
+
+ return query;
+}
+
+/*
+ * register_delta_ENRs
+ *
+ * For all modified tables, make ENRs for their transition tables
+ * and register them to the queryEnv. ENR's RTEs are also appended
+ * into the list in query tree.
+ */
+static void
+register_delta_ENRs(ParseState *pstate, Query *query, List *tables)
+{
+ QueryEnvironment *queryEnv = pstate->p_queryEnv;
+ ListCell *lc;
+ RangeTblEntry *rte;
+
+ foreach(lc, tables)
+ {
+ MV_TriggerTable *table = (MV_TriggerTable *) lfirst(lc);
+ ListCell *lc2;
+ int count;
+
+ count = 0;
+ foreach(lc2, table->old_tuplestores)
+ {
+ Tuplestorestate *oldtable = (Tuplestorestate *) lfirst(lc2);
+ EphemeralNamedRelation enr =
+ palloc(sizeof(EphemeralNamedRelationData));
+ ParseNamespaceItem *nsitem;
+
+ enr->md.name = make_delta_enr_name("old", table->table_id, count);
+ enr->md.reliddesc = table->table_id;
+ enr->md.tupdesc = NULL;
+ enr->md.enrtype = ENR_NAMED_TUPLESTORE;
+ enr->md.enrtuples = tuplestore_tuple_count(oldtable);
+ enr->reldata = oldtable;
+ register_ENR(queryEnv, enr);
+
+ nsitem = addRangeTableEntryForENR(pstate, makeRangeVar(NULL, enr->md.name, -1), true);
+ rte = nsitem->p_rte;
+
+ query->rtable = lappend(query->rtable, rte);
+
+ count++;
+ }
+
+ count = 0;
+ foreach(lc2, table->new_tuplestores)
+ {
+ Tuplestorestate *newtable = (Tuplestorestate *) lfirst(lc2);
+ EphemeralNamedRelation enr =
+ palloc(sizeof(EphemeralNamedRelationData));
+ ParseNamespaceItem *nsitem;
+
+ enr->md.name = make_delta_enr_name("new", table->table_id, count);
+ enr->md.reliddesc = table->table_id;
+ enr->md.tupdesc = NULL;
+ enr->md.enrtype = ENR_NAMED_TUPLESTORE;
+ enr->md.enrtuples = tuplestore_tuple_count(newtable);
+ enr->reldata = newtable;
+ register_ENR(queryEnv, enr);
+
+ nsitem = addRangeTableEntryForENR(pstate, makeRangeVar(NULL, enr->md.name, -1), true);
+ rte = nsitem->p_rte;
+
+ query->rtable = lappend(query->rtable, rte);
+
+ count++;
+ }
+ }
+}
+
+#define DatumGetItemPointer(X) ((ItemPointer) DatumGetPointer(X))
+#define PG_GETARG_ITEMPOINTER(n) DatumGetItemPointer(PG_GETARG_DATUM(n))
+
+/*
+ * ivm_visible_in_prestate
+ *
+ * Check visibility of a tuple specified by the tableoid and item pointer
+ * using the snapshot taken just before the table was modified.
+ */
+Datum
+ivm_visible_in_prestate(PG_FUNCTION_ARGS)
+{
+ Oid tableoid = PG_GETARG_OID(0);
+ ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(1);
+ Oid matviewOid = PG_GETARG_OID(2);
+ MV_TriggerHashEntry *entry;
+ MV_TriggerTable *table = NULL;
+ ListCell *lc;
+ bool found;
+ bool result;
+
+ if (!in_delta_calculation)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("ivm_visible_in_prestate can be called only in delta calculation")));
+
+ entry = (MV_TriggerHashEntry *) hash_search(mv_trigger_info,
+ (void *) &matviewOid,
+ HASH_FIND, &found);
+ Assert (found && entry != NULL);
+
+ foreach(lc, entry->tables)
+ {
+ table = (MV_TriggerTable *) lfirst(lc);
+ if (table->table_id == tableoid)
+ break;
+ }
+
+ Assert (table != NULL);
+
+ result = table_tuple_fetch_row_version(table->rel, itemPtr, entry->snapshot, table->slot);
+
+ PG_RETURN_BOOL(result);
+}
+
+/*
+ * get_prestate_rte
+ *
+ * Rewrite RTE of the modified table to a subquery which represents
+ * "pre-state" table. The original RTE is saved in table->rte_original.
+ */
+static RangeTblEntry*
+get_prestate_rte(RangeTblEntry *rte, MV_TriggerTable *table,
+ QueryEnvironment *queryEnv, Oid matviewid)
+{
+ StringInfoData str;
+ RawStmt *raw;
+ Query *subquery;
+ Relation rel;
+ ParseState *pstate;
+ char *relname;
+ int i;
+
+ pstate = make_parsestate(NULL);
+ pstate->p_queryEnv = queryEnv;
+ pstate->p_expr_kind = EXPR_KIND_SELECT_TARGET;
+
+ /*
+ * We can use NoLock here since AcquireRewriteLocks should
+ * have locked the relation already.
+ */
+ rel = table_open(table->table_id, NoLock);
+ relname = quote_qualified_identifier(
+ get_namespace_name(RelationGetNamespace(rel)),
+ RelationGetRelationName(rel));
+ table_close(rel, NoLock);
+
+ /*
+ * Filtering inserted row using the snapshot taken before the table
+ * is modified. ctid is required for maintaining outer join views.
+ */
+ initStringInfo(&str);
+ appendStringInfo(&str,
+ "SELECT t.* FROM %s t"
+ " WHERE pg_catalog.ivm_visible_in_prestate(t.tableoid, t.ctid ,%d::pg_catalog.oid)",
+ relname, matviewid);
+
+ /*
+ * Append deleted rows contained in old transition tables.
+ */
+ for (i = 0; i < list_length(table->old_tuplestores); i++)
+ {
+ appendStringInfo(&str, " UNION ALL ");
+ appendStringInfo(&str," SELECT * FROM %s",
+ make_delta_enr_name("old", table->table_id, i));
+ }
+
+ /* Get a subquery representing pre-state of the table */
+ raw = (RawStmt*)linitial(raw_parser(str.data, RAW_PARSE_DEFAULT));
+ subquery = transformStmt(pstate, raw->stmt);
+
+ /* save the original RTE */
+ table->original_rte = copyObject(rte);
+
+ rte->rtekind = RTE_SUBQUERY;
+ rte->subquery = subquery;
+ rte->security_barrier = false;
+
+ /* Clear fields that should not be set in a subquery RTE */
+ rte->relid = InvalidOid;
+ rte->relkind = 0;
+ rte->rellockmode = 0;
+ rte->tablesample = NULL;
+ rte->perminfoindex = 0; /* no permission checking for this RTE */
+ rte->inh = false; /* must not be set for a subquery */
+
+ return rte;
+}
+
+/*
+ * make_delta_enr_name
+ *
+ * Make a name for ENR of a transition table from the base table's oid.
+ * prefix will be "new" or "old" depending on its transition table kind..
+ */
+static char*
+make_delta_enr_name(const char *prefix, Oid relid, int count)
+{
+ char buf[NAMEDATALEN];
+ char *name;
+
+ snprintf(buf, NAMEDATALEN, "__ivm_%s_%u_%u", prefix, relid, count);
+ name = pstrdup(buf);
+
+ return name;
+}
+
+/*
+ * replace_rte_with_delta
+ *
+ * Replace RTE of the modified table with a single table delta that combine its
+ * all transition tables.
+ */
+static RangeTblEntry*
+replace_rte_with_delta(RangeTblEntry *rte, MV_TriggerTable *table, bool is_new,
+ QueryEnvironment *queryEnv)
+{
+ Oid relid = table->table_id;
+ StringInfoData str;
+ ParseState *pstate;
+ RawStmt *raw;
+ Query *sub;
+ int num_tuplestores = list_length(is_new ? table->new_tuplestores : table->old_tuplestores);
+ int i;
+
+ /* the previous RTE must be a subquery which represents "pre-state" table */
+ Assert(rte->rtekind == RTE_SUBQUERY);
+
+ /* Create a ParseState for rewriting the view definition query */
+ pstate = make_parsestate(NULL);
+ pstate->p_queryEnv = queryEnv;
+ pstate->p_expr_kind = EXPR_KIND_SELECT_TARGET;
+
+ initStringInfo(&str);
+
+ for (i = 0; i < num_tuplestores; i++)
+ {
+ if (i > 0)
+ appendStringInfo(&str, " UNION ALL ");
+
+ appendStringInfo(&str,
+ " SELECT * FROM %s",
+ make_delta_enr_name(is_new ? "new" : "old", relid, i));
+ }
+
+ raw = (RawStmt*)linitial(raw_parser(str.data, RAW_PARSE_DEFAULT));
+ sub = transformStmt(pstate, raw->stmt);
+
+ /*
+ * Update the subquery so that it represent the combined transition
+ * table. Note that we leave the security_barrier and securityQuals
+ * fields so that the subquery relation can be protected by the RLS
+ * policy as same as the modified table.
+ */
+ rte->rtekind = RTE_SUBQUERY;
+ rte->subquery = sub;
+
+ return rte;
+}
+
+/*
+ * rewrite_query_for_counting
+ *
+ * Rewrite query for counting duplicated tuples.
+ */
+static Query *
+rewrite_query_for_counting(Query *query, ParseState *pstate)
+{
+ TargetEntry *tle_count;
+ FuncCall *fn;
+ Node *node;
+
+ /* Add count(*) for counting distinct tuples in views */
+ fn = makeFuncCall(SystemFuncName("count"), NIL, COERCE_EXPLICIT_CALL, -1);
+ fn->agg_star = true;
+ if (!query->groupClause && !query->hasAggs)
+ query->groupClause = transformDistinctClause(NULL, &query->targetList, query->sortClause, false);
+
+ node = ParseFuncOrColumn(pstate, fn->funcname, NIL, NULL, fn, false, -1);
+
+ tle_count = makeTargetEntry((Expr *) node,
+ list_length(query->targetList) + 1,
+ pstrdup("__ivm_count__"),
+ false);
+ query->targetList = lappend(query->targetList, tle_count);
+ query->hasAggs = true;
+
+ return query;
+}
+
+/*
+ * calc_delta
+ *
+ * Calculate view deltas generated under the modification of a table specified
+ * by the RTE index.
+ */
+static void
+calc_delta(MV_TriggerTable *table, int rte_index, Query *query,
+ DestReceiver *dest_old, DestReceiver *dest_new,
+ TupleDesc *tupdesc_old, TupleDesc *tupdesc_new,
+ QueryEnvironment *queryEnv)
+{
+ ListCell *lc = list_nth_cell(query->rtable, rte_index - 1);
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
+
+ in_delta_calculation = true;
+
+ /* Generate old delta */
+ if (list_length(table->old_tuplestores) > 0)
+ {
+ /* Replace the modified table with the old delta table and calculate the old view delta. */
+ replace_rte_with_delta(rte, table, false, queryEnv);
+ refresh_matview_datafill(dest_old, query, queryEnv, tupdesc_old, "");
+ }
+
+ /* Generate new delta */
+ if (list_length(table->new_tuplestores) > 0)
+ {
+ /* Replace the modified table with the new delta table and calculate the new view delta*/
+ replace_rte_with_delta(rte, table, true, queryEnv);
+ refresh_matview_datafill(dest_new, query, queryEnv, tupdesc_new, "");
+ }
+
+ in_delta_calculation = false;
+}
+
+/*
+ * rewrite_query_for_postupdate_state
+ *
+ * Rewrite the query so that the specified base table's RTEs will represent
+ * "post-update" state of tables. This is called after the view delta
+ * calculation due to changes on this table finishes.
+ */
+static Query*
+rewrite_query_for_postupdate_state(Query *query, MV_TriggerTable *table, int rte_index)
+{
+ ListCell *lc = list_nth_cell(query->rtable, rte_index - 1);
+
+ /* Retore the original RTE */
+ lfirst(lc) = table->original_rte;
+
+ return query;
+}
+
+/*
+ * apply_delta
+ *
+ * Apply deltas to the materialized view. In outer join cases, this requires
+ * the view maintenance graph.
+ */
+static void
+apply_delta(Oid matviewOid, Tuplestorestate *old_tuplestores, Tuplestorestate *new_tuplestores,
+ TupleDesc tupdesc_old, TupleDesc tupdesc_new,
+ Query *query)
+{
+ StringInfoData querybuf;
+ StringInfoData target_list_buf;
+ Relation matviewRel;
+ char *matviewname;
+ ListCell *lc;
+ int i;
+ List *keys = NIL;
+
+
+ /*
+ * get names of the materialized view and delta tables
+ */
+
+ matviewRel = table_open(matviewOid, NoLock);
+ matviewname = quote_qualified_identifier(get_namespace_name(RelationGetNamespace(matviewRel)),
+ RelationGetRelationName(matviewRel));
+
+ /*
+ * Build parts of the maintenance queries
+ */
+
+ initStringInfo(&querybuf);
+ initStringInfo(&target_list_buf);
+
+ /* build string of target list */
+ for (i = 0; i < matviewRel->rd_att->natts; i++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(matviewRel->rd_att, i);
+ char *resname = NameStr(attr->attname);
+
+ if (i != 0)
+ appendStringInfo(&target_list_buf, ", ");
+ appendStringInfo(&target_list_buf, "%s", quote_qualified_identifier(NULL, resname));
+ }
+
+ i = 0;
+ foreach (lc, query->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+ Form_pg_attribute attr = TupleDescAttr(matviewRel->rd_att, i);
+
+ i++;
+
+ if (tle->resjunk)
+ continue;
+
+ keys = lappend(keys, attr);
+ }
+
+ /* Start maintaining the materialized view. */
+ OpenMatViewIncrementalMaintenance();
+
+ /* Open SPI context. */
+ if (SPI_connect() != SPI_OK_CONNECT)
+ elog(ERROR, "SPI_connect failed");
+
+ /* For tuple deletion */
+ if (old_tuplestores && tuplestore_tuple_count(old_tuplestores) > 0)
+ {
+ EphemeralNamedRelation enr = palloc(sizeof(EphemeralNamedRelationData));
+ int rc;
+
+ /* convert tuplestores to ENR, and register for SPI */
+ enr->md.name = pstrdup(OLD_DELTA_ENRNAME);
+ enr->md.reliddesc = InvalidOid;
+ enr->md.tupdesc = tupdesc_old;
+ enr->md.enrtype = ENR_NAMED_TUPLESTORE;
+ enr->md.enrtuples = tuplestore_tuple_count(old_tuplestores);
+ enr->reldata = old_tuplestores;
+
+ rc = SPI_register_relation(enr);
+ if (rc != SPI_OK_REL_REGISTER)
+ elog(ERROR, "SPI_register failed");
+
+ apply_old_delta(matviewname, OLD_DELTA_ENRNAME, keys);
+
+ }
+ /* For tuple insertion */
+ if (new_tuplestores && tuplestore_tuple_count(new_tuplestores) > 0)
+ {
+ EphemeralNamedRelation enr = palloc(sizeof(EphemeralNamedRelationData));
+ int rc;
+
+ /* convert tuplestores to ENR, and register for SPI */
+ enr->md.name = pstrdup(NEW_DELTA_ENRNAME);
+ enr->md.reliddesc = InvalidOid;
+ enr->md.tupdesc = tupdesc_new;;
+ enr->md.enrtype = ENR_NAMED_TUPLESTORE;
+ enr->md.enrtuples = tuplestore_tuple_count(new_tuplestores);
+ enr->reldata = new_tuplestores;
+
+ rc = SPI_register_relation(enr);
+ if (rc != SPI_OK_REL_REGISTER)
+ elog(ERROR, "SPI_register failed");
+
+ /* apply new delta */
+ apply_new_delta(matviewname, NEW_DELTA_ENRNAME, &target_list_buf);
+ }
+
+ /* We're done maintaining the materialized view. */
+ CloseMatViewIncrementalMaintenance();
+
+ table_close(matviewRel, NoLock);
+
+ /* Close SPI context. */
+ if (SPI_finish() != SPI_OK_FINISH)
+ elog(ERROR, "SPI_finish failed");
+}
+
+/*
+ * apply_old_delta
+ *
+ * Execute a query for applying a delta table given by deltname_old
+ * which contains tuples to be deleted from to a materialized view given by
+ * matviewname. This is used when counting is not required.
+ */
+static void
+apply_old_delta(const char *matviewname, const char *deltaname_old,
+ List *keys)
+{
+ StringInfoData querybuf;
+ StringInfoData keysbuf;
+ char *match_cond;
+ ListCell *lc;
+
+ /* build WHERE condition for searching tuples to be deleted */
+ match_cond = get_matching_condition_string(keys);
+
+ /* build string of keys list */
+ initStringInfo(&keysbuf);
+ foreach (lc, keys)
+ {
+ Form_pg_attribute attr = (Form_pg_attribute) lfirst(lc);
+ char *resname = NameStr(attr->attname);
+ appendStringInfo(&keysbuf, "%s", quote_qualified_identifier("mv", resname));
+ if (lnext(keys, lc))
+ appendStringInfo(&keysbuf, ", ");
+ }
+
+ /* Search for matching tuples from the view and update or delete if found. */
+ initStringInfo(&querybuf);
+ appendStringInfo(&querybuf,
+ "DELETE FROM %s WHERE ctid IN ("
+ "SELECT tid FROM (SELECT pg_catalog.row_number() over (partition by %s) AS \"__ivm_row_number__\","
+ "mv.ctid AS tid,"
+ "diff.\"__ivm_count__\""
+ "FROM %s AS mv, %s AS diff "
+ "WHERE %s) v "
+ "WHERE v.\"__ivm_row_number__\" OPERATOR(pg_catalog.<=) v.\"__ivm_count__\")",
+ matviewname,
+ keysbuf.data,
+ matviewname, deltaname_old,
+ match_cond);
+
+ if (SPI_exec(querybuf.data, 0) != SPI_OK_DELETE)
+ elog(ERROR, "SPI_exec failed: %s", querybuf.data);
+}
+
+/*
+ * apply_new_delta
+ *
+ * Execute a query for applying a delta table given by deltname_new
+ * which contains tuples to be inserted into a materialized view given by
+ * matviewname. This is used when counting is not required.
+ */
+static void
+apply_new_delta(const char *matviewname, const char *deltaname_new,
+ StringInfo target_list)
+{
+ StringInfoData querybuf;
+
+ /* Search for matching tuples from the view and update or delete if found. */
+ initStringInfo(&querybuf);
+ appendStringInfo(&querybuf,
+ "INSERT INTO %s (%s) SELECT %s FROM ("
+ "SELECT diff.*, pg_catalog.generate_series(1, diff.\"__ivm_count__\")"
+ " AS __ivm_generate_series__ "
+ "FROM %s AS diff) AS v",
+ matviewname, target_list->data, target_list->data,
+ deltaname_new);
+
+ if (SPI_exec(querybuf.data, 0) != SPI_OK_INSERT)
+ elog(ERROR, "SPI_exec failed: %s", querybuf.data);
+}
+
+/*
+ * get_matching_condition_string
+ *
+ * Build a predicate string for looking for a tuple with given keys.
+ */
+static char *
+get_matching_condition_string(List *keys)
+{
+ StringInfoData match_cond;
+ ListCell *lc;
+
+ /* If there is no key columns, the condition is always true. */
+ if (keys == NIL)
+ return "true";
+
+ initStringInfo(&match_cond);
+ foreach (lc, keys)
+ {
+ Form_pg_attribute attr = (Form_pg_attribute) lfirst(lc);
+ char *resname = NameStr(attr->attname);
+ char *mv_resname = quote_qualified_identifier("mv", resname);
+ char *diff_resname = quote_qualified_identifier("diff", resname);
+ Oid typid = attr->atttypid;
+
+ /* Considering NULL values, we can not use simple = operator. */
+ appendStringInfo(&match_cond, "(");
+ generate_equal(&match_cond, typid, mv_resname, diff_resname);
+ appendStringInfo(&match_cond, " OR (%s IS NULL AND %s IS NULL))",
+ mv_resname, diff_resname);
+
+ if (lnext(keys, lc))
+ appendStringInfo(&match_cond, " AND ");
+ }
+
+ return match_cond.data;
+}
+
+/*
+ * generate_equals
+ *
+ * Generate an equality clause using given operands' default equality
+ * operator.
+ */
+static void
+generate_equal(StringInfo querybuf, Oid opttype,
+ const char *leftop, const char *rightop)
+{
+ TypeCacheEntry *typentry;
+
+ typentry = lookup_type_cache(opttype, TYPECACHE_EQ_OPR);
+ if (!OidIsValid(typentry->eq_opr))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be_qualified(opttype))));
+
+ generate_operator_clause(querybuf,
+ leftop, opttype,
+ typentry->eq_opr,
+ rightop, opttype);
+}
+
+/*
+ * mv_InitHashTables
+ */
+static void
+mv_InitHashTables(void)
+{
+ HASHCTL ctl;
+
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(MV_TriggerHashEntry);
+ mv_trigger_info = hash_create("MV trigger info",
+ MV_INIT_QUERYHASHSIZE,
+ &ctl, HASH_ELEM | HASH_BLOBS);
+}
+
+/*
+ * AtAbort_IVM
+ *
+ * Clean up hash entries for all materialized views. This is called at
+ * transaction abort.
+ */
+void
+AtAbort_IVM()
+{
+ HASH_SEQ_STATUS seq;
+ MV_TriggerHashEntry *entry;
+
+ if (mv_trigger_info)
+ {
+ hash_seq_init(&seq, mv_trigger_info);
+ while ((entry = hash_seq_search(&seq)) != NULL)
+ clean_up_IVM_hash_entry(entry, true);
+ }
+ in_delta_calculation = false;
+}
+
+/*
+ * clean_up_IVM_hash_entry
+ *
+ * Clean up tuple stores and hash entries for a materialized view after its
+ * maintenance finished.
+ */
+static void
+clean_up_IVM_hash_entry(MV_TriggerHashEntry *entry, bool is_abort)
+{
+ bool found;
+ ListCell *lc;
+
+ foreach(lc, entry->tables)
+ {
+ MV_TriggerTable *table = (MV_TriggerTable *) lfirst(lc);
+
+ list_free(table->old_tuplestores);
+ list_free(table->new_tuplestores);
+ if (!is_abort)
+ {
+ ExecDropSingleTupleTableSlot(table->slot);
+ table_close(table->rel, NoLock);
+ }
+ }
+ list_free(entry->tables);
+
+ if (!is_abort)
+ UnregisterSnapshot(entry->snapshot);
+
+ hash_search(mv_trigger_info, (void *) &entry->matview_id, HASH_REMOVE, &found);
+}
+
+/*
+ * isIvmName
+ *
+ * Check if this is a IVM hidden column from the name.
+ */
+bool
+isIvmName(const char *s)
+{
+ if (s)
+ return (strncmp(s, "__ivm_", 6) == 0);
+ return false;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 9c120fc2b7..e111f11bf5 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12174,4 +12174,14 @@
proargtypes => 'int2',
prosrc => 'gist_stratnum_identity' },
+# IVM
+{ oid => '786', descr => 'ivm trigger (before)',
+ proname => 'IVM_immediate_before', provolatile => 'v', prorettype => 'trigger',
+ proargtypes => '', prosrc => 'IVM_immediate_before' },
+{ oid => '787', descr => 'ivm trigger (after)',
+ proname => 'IVM_immediate_maintenance', provolatile => 'v', prorettype => 'trigger',
+ proargtypes => '', prosrc => 'IVM_immediate_maintenance' },
+{ oid => '788', descr => 'ivm filetring ',
+ proname => 'ivm_visible_in_prestate', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid tid oid', prosrc => 'ivm_visible_in_prestate' },
]
diff --git a/src/include/commands/createas.h b/src/include/commands/createas.h
index 94678e3834..396ad1bb4c 100644
--- a/src/include/commands/createas.h
+++ b/src/include/commands/createas.h
@@ -16,6 +16,7 @@
#include "catalog/objectaddress.h"
#include "nodes/params.h"
+#include "nodes/pathnodes.h"
#include "parser/parse_node.h"
#include "tcop/dest.h"
#include "utils/queryenvironment.h"
@@ -25,6 +26,9 @@ extern ObjectAddress ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *st
ParamListInfo params, QueryEnvironment *queryEnv,
QueryCompletion *qc);
+extern void CreateIvmTriggersOnBaseTables(Query *qry, Oid matviewOid);
+extern void CreateIndexOnIMMV(Query *query, Relation matviewRel);
+
extern int GetIntoRelEFlags(IntoClause *intoClause);
extern DestReceiver *CreateIntoRelDestReceiver(IntoClause *intoClause);
diff --git a/src/include/commands/matview.h b/src/include/commands/matview.h
index 817b2ba0b6..3257e1adff 100644
--- a/src/include/commands/matview.h
+++ b/src/include/commands/matview.h
@@ -15,6 +15,7 @@
#define MATVIEW_H
#include "catalog/objectaddress.h"
+#include "fmgr.h"
#include "nodes/params.h"
#include "nodes/parsenodes.h"
#include "tcop/dest.h"
@@ -23,6 +24,8 @@
extern void SetMatViewPopulatedState(Relation relation, bool newstate);
+extern void SetMatViewIVMState(Relation relation, bool newstate);
+
extern ObjectAddress ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
ParamListInfo params, QueryCompletion *qc);
@@ -30,4 +33,10 @@ extern DestReceiver *CreateTransientRelDestReceiver(Oid transientoid);
extern bool MatViewIncrementalMaintenanceIsEnabled(void);
+extern Datum IVM_immediate_before(PG_FUNCTION_ARGS);
+extern Datum IVM_immediate_maintenance(PG_FUNCTION_ARGS);
+extern Datum IVM_visible_in_prestate(PG_FUNCTION_ARGS);
+extern void AtAbort_IVM(void);
+extern bool isIvmName(const char *s);
+
#endif /* MATVIEW_H */
--
2.25.1
--Multipart=_Mon__4_Mar_2024_11_58_46_+0900_UaponF/qQhQrVCFt
Content-Type: text/x-diff;
name="v30-0007-Add-DISTINCT-support-for-IVM.patch"
Content-Disposition: attachment;
filename="v30-0007-Add-DISTINCT-support-for-IVM.patch"
Content-Transfer-Encoding: 7bit
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 376+ messages in thread
* Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 06:11 Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
0 siblings, 1 reply; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 06:11 UTC (permalink / raw)
To: pgsql-hackers
Hi,
I would like to propose adding new fields to pg_stat_all_tables to track
tables skipped during autovacuum and autoanalyze.
Currently, when autovacuum or autoanalyze is skipped because a lock cannot
be acquired, this information is only emitted in log messages. However, it
would be more useful if users could access this information via a system view,
alongside related fields such as last_autovacuum, on a per-table basis.
The attached patch add the following fields to pg_stat_all_tables:
- last_skipped_autovacuum
- last_skipped_autoanalyze
- skipped_autovacuum_count
- skipped_autoanalyze_count
Are there any concerns about exposing this in pg_stat_all_tables, or suggestions
for a better approach?
Regards,
Yugo Nagata
--
Yugo Nagata <nagata@sraoss.co.jp>
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
@ 2026-03-24 14:58 ` Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 1 reply; 376+ messages in thread
From: Sami Imseih @ 2026-03-24 14:58 UTC (permalink / raw)
To: Yugo Nagata <nagata@sraoss.co.jp>; +Cc: pgsql-hackers
Hi,
Thanks for the patch!
> The attached patch add the following fields to pg_stat_all_tables:
> - last_skipped_autovacuum
> - last_skipped_autoanalyze
> - skipped_autovacuum_count
> - skipped_autoanalyze_count
>
> Are there any concerns about exposing this in pg_stat_all_tables, or suggestions
> for a better approach?
I am not sure about the timestamp columns. I am not saying they will
not be useful,
but I think it will be better to just start with counters for this.
The way the views get
used, a dashboard built for tracking the deltas of the counters can easily spot
when there is a spike of skipped autovacuum/autoanalyze count.
Also, for tables that are being autovacuumed and skipped quickly,
the timestamps will just be overwritten.
So, I am +1 on the counters, -1 on the timestamps.
Out of scope for this patch, but I also wonder if we should add another counter,
autovacuum_started_count. If there are other types of failure scenarios such as
corrupt indexes, checksum failures, etc. which terminate the
autovacuum in flight,
we would be able to catch this by looking at the number of autovacuums
started vs completed. The skipped counters in this patch and a started
counter would capture different stages of the autovacuum lifecycle;
skipped means
"never started" (lock contention), while a started-minus-completed delta means
"started but failed." Both are useful signals, but for different reasons.
In terms of the patch:
1/
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
Any reason why this should not also include manual vacuum/analyze?
If someone has a vacuum/analyze script that uses SKIP_LOCKED, and
the operation gets skipped, this should be included in the counter.
this can be done with separate counter fields for autovacuum/autoanalyze and
vacuum/analyze
2/
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS
skipped_autovacuum_count,
How about a name like "autovacuum_lock_skip_count"?
--
Sami Imseih
Amazon Web Services (AWS)
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
@ 2026-03-24 16:28 ` Yugo Nagata <nagata@sraoss.co.jp>
2026-03-25 02:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 2 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-24 16:28 UTC (permalink / raw)
To: Sami Imseih <samimseih@gmail.com>; +Cc: pgsql-hackers
On Tue, 24 Mar 2026 09:58:48 -0500
Sami Imseih <samimseih@gmail.com> wrote:
Thank you for your comments!
> > The attached patch add the following fields to pg_stat_all_tables:
> > - last_skipped_autovacuum
> > - last_skipped_autoanalyze
> > - skipped_autovacuum_count
> > - skipped_autoanalyze_count
> >
> > Are there any concerns about exposing this in pg_stat_all_tables, or suggestions
> > for a better approach?
>
> I am not sure about the timestamp columns. I am not saying they will
> not be useful,
> but I think it will be better to just start with counters for this.
> The way the views get
> used, a dashboard built for tracking the deltas of the counters can easily spot
> when there is a spike of skipped autovacuum/autoanalyze count.
> Also, for tables that are being autovacuumed and skipped quickly,
> the timestamps will just be overwritten.
>
> So, I am +1 on the counters, -1 on the timestamps.
Although the timestamps are overwritten on each skipped autovacuum or
autoanalyze, they still indicate when the last attempt was made. This
can help users confirm that autovacuum is actively attempting to run,
and that the issue is due to repeated skips rather than inactivity.
While counters can indicate overall activity, they do not reveal when
the last skip occurred. With timestamps, users can immediately see the
most recent attempt, even without a separate dashboard or historical
tracking.
Therefore, counters are useful for monitoring overall activity, but
timestamps give additional, complementary information, so it seems
worthwhile to include them too.
> Out of scope for this patch, but I also wonder if we should add another counter,
> autovacuum_started_count. If there are other types of failure scenarios such as
> corrupt indexes, checksum failures, etc. which terminate the
> autovacuum in flight,
> we would be able to catch this by looking at the number of autovacuums
> started vs completed. The skipped counters in this patch and a started
> counter would capture different stages of the autovacuum lifecycle;
> skipped means
> "never started" (lock contention), while a started-minus-completed delta means
> "started but failed." Both are useful signals, but for different reasons.
That makes sense. I was considering adding a counter to track
"canceled" autovacuum or autoanalyze, but tracking the number of started
or attempted autovacuums might provide a more general information than counting
only the failed ones.
> In terms of the patch:
>
> 1/
>
> + if (AmAutoVacuumWorkerProcess())
> + pgstat_report_skipped_vacuum(relid);
>
> Any reason why this should not also include manual vacuum/analyze?
> If someone has a vacuum/analyze script that uses SKIP_LOCKED, and
> the operation gets skipped, this should be included in the counter.
> this can be done with separate counter fields for autovacuum/autoanalyze and
> vacuum/analyze
For manual vacuum/analyze, an explicit WARNING is output when the
operation is skipped, so I initially thought that reporting it in the
stats view was not necessary. However, I now agree that it should be
included.
> 2/
>
> + pg_stat_get_skipped_autovacuum_count(C.oid) AS
> skipped_autovacuum_count,
>
> How about a name like "autovacuum_lock_skip_count"?
I'm not sure this is a good fit, since it may be better to keep the
naming consistent with other fields whose names end with
"...vacuum_count" or "...analyze_count". Happy to hear other opinions.
Regards,
Yugo Nagata
--
Yugo Nagata <nagata@sraoss.co.jp>
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
@ 2026-03-25 02:07 ` Michael Paquier <michael@paquier.xyz>
2026-03-25 04:24 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
1 sibling, 1 reply; 376+ messages in thread
From: Michael Paquier @ 2026-03-25 02:07 UTC (permalink / raw)
To: Yugo Nagata <nagata@sraoss.co.jp>; +Cc: Sami Imseih <samimseih@gmail.com>; pgsql-hackers
On Wed, Mar 25, 2026 at 01:28:47AM +0900, Yugo Nagata wrote:
> Although the timestamps are overwritten on each skipped autovacuum or
> autoanalyze, they still indicate when the last attempt was made. This
> can help users confirm that autovacuum is actively attempting to run,
> and that the issue is due to repeated skips rather than inactivity.
>
> While counters can indicate overall activity, they do not reveal when
> the last skip occurred. With timestamps, users can immediately see the
> most recent attempt, even without a separate dashboard or historical
> tracking.
>
> Therefore, counters are useful for monitoring overall activity, but
> timestamps give additional, complementary information, so it seems
> worthwhile to include them too.
Hmm.. I can buy this argument for the timestamps, especially for
database with many relations of various sizes that could take a
various amount of time to process. The timestamps could offer hints
about the time it takes between the skips, even if snapshots of the
stats data are not taken at a very aggressive frequency.
This is v20 material at this stage, of course..
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, ../../acNDTM20nJjDYZxq@paquier.xyz/2-signature.asc)
download
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-25 02:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
@ 2026-03-25 04:24 ` Yugo Nagata <nagata@sraoss.co.jp>
2026-03-25 17:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
0 siblings, 1 reply; 376+ messages in thread
From: Yugo Nagata @ 2026-03-25 04:24 UTC (permalink / raw)
To: Michael Paquier <michael@paquier.xyz>; +Cc: Sami Imseih <samimseih@gmail.com>; pgsql-hackers
On Wed, 25 Mar 2026 11:07:08 +0900
Michael Paquier <michael@paquier.xyz> wrote:
> On Wed, Mar 25, 2026 at 01:28:47AM +0900, Yugo Nagata wrote:
> > Although the timestamps are overwritten on each skipped autovacuum or
> > autoanalyze, they still indicate when the last attempt was made. This
> > can help users confirm that autovacuum is actively attempting to run,
> > and that the issue is due to repeated skips rather than inactivity.
> >
> > While counters can indicate overall activity, they do not reveal when
> > the last skip occurred. With timestamps, users can immediately see the
> > most recent attempt, even without a separate dashboard or historical
> > tracking.
> >
> > Therefore, counters are useful for monitoring overall activity, but
> > timestamps give additional, complementary information, so it seems
> > worthwhile to include them too.
>
> Hmm.. I can buy this argument for the timestamps, especially for
> database with many relations of various sizes that could take a
> various amount of time to process. The timestamps could offer hints
> about the time it takes between the skips, even if snapshots of the
> stats data are not taken at a very aggressive frequency.
>
> This is v20 material at this stage, of course..
Thank you for your comments.
Yes, I understand that it's too late for this to be included in v19.
Regards,
Yugo Nagata
--
Yugo Nagata <nagata@sraoss.co.jp>
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-25 02:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-25 04:24 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
@ 2026-03-25 17:12 ` Sami Imseih <samimseih@gmail.com>
2026-03-25 23:26 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
0 siblings, 1 reply; 376+ messages in thread
From: Sami Imseih @ 2026-03-25 17:12 UTC (permalink / raw)
To: Yugo Nagata <nagata@sraoss.co.jp>; +Cc: Michael Paquier <michael@paquier.xyz>; pgsql-hackers
> > On Wed, Mar 25, 2026 at 01:28:47AM +0900, Yugo Nagata wrote:
> > > Although the timestamps are overwritten on each skipped autovacuum or
> > > autoanalyze, they still indicate when the last attempt was made. This
> > > can help users confirm that autovacuum is actively attempting to run,
> > > and that the issue is due to repeated skips rather than inactivity.
> > >
> > > While counters can indicate overall activity, they do not reveal when
> > > the last skip occurred. With timestamps, users can immediately see the
> > > most recent attempt, even without a separate dashboard or historical
> > > tracking.
> > >
> > > Therefore, counters are useful for monitoring overall activity, but
> > > timestamps give additional, complementary information, so it seems
> > > worthwhile to include them too.
> >
> > Hmm.. I can buy this argument for the timestamps, especially for
> > database with many relations of various sizes that could take a
> > various amount of time to process. The timestamps could offer hints
> > about the time it takes between the skips, even if snapshots of the
> > stats data are not taken at a very aggressive frequency.
I'm fine with adding timestamps, as there seem to be convincing
reasons to add them.
My other concern is bloat of the pg_stat_all_tables view. This patch
adds 4 columns, or
8 if we also include manual vacuum and analyze (which I think we should).
Given that, should we also start thinking about splitting the vacuum
activity related
columns into a dedicated view and out of pg_stat_all_tables for v20?
--
Sami Imseih
Amazon Web Services (AWS)
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-25 02:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-25 04:24 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-25 17:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
@ 2026-03-25 23:26 ` Michael Paquier <michael@paquier.xyz>
2026-03-26 01:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 1 reply; 376+ messages in thread
From: Michael Paquier @ 2026-03-25 23:26 UTC (permalink / raw)
To: Sami Imseih <samimseih@gmail.com>; +Cc: Yugo Nagata <nagata@sraoss.co.jp>; pgsql-hackers
On Wed, Mar 25, 2026 at 12:12:35PM -0500, Sami Imseih wrote:
> I'm fine with adding timestamps, as there seem to be convincing
> reasons to add them.
> My other concern is bloat of the pg_stat_all_tables view. This patch
> adds 4 columns, or
> 8 if we also include manual vacuum and analyze (which I think we should).
>
> Given that, should we also start thinking about splitting the vacuum
> activity related
> columns into a dedicated view and out of pg_stat_all_tables for v20?
PgStat_StatTabEntry is shared between indexes and tables. A bunch of
its fields apply only to tables, not indexes (aka all the vacuum and
analyze ones). Few fields apply only to indexes, not tables. Not
that many are shared between both. I would advocate for a clean split
between indexes and tables, as a start, with a new variable-sized
stats kind dedicated to indexes.
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, ../../acRvIt2idOKeD7wv@paquier.xyz/2-signature.asc)
download
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-25 02:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-25 04:24 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-25 17:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-25 23:26 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
@ 2026-03-26 01:18 ` Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 01:31 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
0 siblings, 1 reply; 376+ messages in thread
From: Yugo Nagata @ 2026-03-26 01:18 UTC (permalink / raw)
To: Michael Paquier <michael@paquier.xyz>; +Cc: Sami Imseih <samimseih@gmail.com>; pgsql-hackers
On Thu, 26 Mar 2026 08:26:26 +0900
Michael Paquier <michael@paquier.xyz> wrote:
> On Wed, Mar 25, 2026 at 12:12:35PM -0500, Sami Imseih wrote:
> > I'm fine with adding timestamps, as there seem to be convincing
> > reasons to add them.
> > My other concern is bloat of the pg_stat_all_tables view. This patch
> > adds 4 columns, or
> > 8 if we also include manual vacuum and analyze (which I think we should).
> >
> > Given that, should we also start thinking about splitting the vacuum
> > activity related
> > columns into a dedicated view and out of pg_stat_all_tables for v20?
>
> PgStat_StatTabEntry is shared between indexes and tables. A bunch of
> its fields apply only to tables, not indexes (aka all the vacuum and
> analyze ones). Few fields apply only to indexes, not tables. Not
> that many are shared between both. I would advocate for a clean split
> between indexes and tables, as a start, with a new variable-sized
> stats kind dedicated to indexes.
I see that the following fields are used in pg_stat_all_indexes:
- numscans
- lastscan
- tuples_returned
- tuples_fetched
- stat_reset_time
but they are also shared with pg_stat_all_tables. Are you suggesting
splitting these shared fields from those that are specific to tables?
I'm not sure this would significantly reduce the size of
PgStat_StatTabEntry. Could you elaborate on the expected benefits?
Regards,
Yugo Nagata
--
Yugo Nagata <nagata@sraoss.co.jp>
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-25 02:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-25 04:24 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-25 17:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-25 23:26 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-26 01:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
@ 2026-03-26 01:31 ` Michael Paquier <michael@paquier.xyz>
2026-03-26 02:25 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 1 reply; 376+ messages in thread
From: Michael Paquier @ 2026-03-26 01:31 UTC (permalink / raw)
To: Yugo Nagata <nagata@sraoss.co.jp>; +Cc: Sami Imseih <samimseih@gmail.com>; pgsql-hackers
On Thu, Mar 26, 2026 at 10:18:39AM +0900, Yugo Nagata wrote:
> I'm not sure this would significantly reduce the size of
> PgStat_StatTabEntry. Could you elaborate on the expected benefits?
The point is that this reduces the shmem footprint for indexes (well,
it's also benefitial for tables, just less), on top of being cleaner
because the stats views would only need to store and query the fields
they care about for each relkind.
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, ../../acSMZ-0K_YUrmzQG@paquier.xyz/2-signature.asc)
download
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-25 02:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-25 04:24 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-25 17:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-25 23:26 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-26 01:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 01:31 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
@ 2026-03-26 02:25 ` Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 0 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-03-26 02:25 UTC (permalink / raw)
To: Michael Paquier <michael@paquier.xyz>; +Cc: Sami Imseih <samimseih@gmail.com>; pgsql-hackers
On Thu, 26 Mar 2026 10:31:19 +0900
Michael Paquier <michael@paquier.xyz> wrote:
> On Thu, Mar 26, 2026 at 10:18:39AM +0900, Yugo Nagata wrote:
> > I'm not sure this would significantly reduce the size of
> > PgStat_StatTabEntry. Could you elaborate on the expected benefits?
>
> The point is that this reduces the shmem footprint for indexes (well,
> it's also benefitial for tables, just less), on top of being cleaner
> because the stats views would only need to store and query the fields
> they care about for each relkind.
Thank you for the clarification. I think I understand your point now.
So, you mean introducing a separate stats kind for indexes, such as
PGSTAT_KIND_INDEX?
Regards,
Yugo Nagata
> --
> Michael
--
Yugo Nagata <nagata@sraoss.co.jp>
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
@ 2026-03-26 10:22 ` Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
1 sibling, 1 reply; 376+ messages in thread
From: Yugo Nagata @ 2026-03-26 10:22 UTC (permalink / raw)
To: Sami Imseih <samimseih@gmail.com>; +Cc: pgsql-hackers; Yugo Nagata <nagata@sraoss.co.jp>; Michael Paquier <michael@paquier.xyz>
> On Tue, 24 Mar 2026 09:58:48 -0500
> Sami Imseih <samimseih@gmail.com> wrote:
> > 1/
> >
> > + if (AmAutoVacuumWorkerProcess())
> > + pgstat_report_skipped_vacuum(relid);
> >
> > Any reason why this should not also include manual vacuum/analyze?
> > If someone has a vacuum/analyze script that uses SKIP_LOCKED, and
> > the operation gets skipped, this should be included in the counter.
> > this can be done with separate counter fields for autovacuum/autoanalyze and
> > vacuum/analyze
>
> For manual vacuum/analyze, an explicit WARNING is output when the
> operation is skipped, so I initially thought that reporting it in the
> stats view was not necessary. However, I now agree that it should be
> included.
I've attached an updated patch to also report skipped manual
vacuum/analyze.
The pgstat reporting functions are unified into a single function,
pgstat_report_skipped_vacuum_analyze(), which handles both
auto/manual and vacuum/analyze cases. Also it is fixed to use
InvalidOid for shared relations.
To support manual vacuum/analyze, some hack were needed to
obtain the relid before the lock attempt. When SKIP_LOCKED is specified,
the relid is obtained using RangeVarGetRelid() with NoLock prior to
locking. If ConditionalLockRelationOid() then fails, the skip is
reported.
To handle the possibility that the table is dropped between
RangeVarGetRelid() and the lock attempt, SearchSysCacheExists1() is
used after acquiring the lock.
Regards,
Yugo Nagata
--
Yugo Nagata <nagata@sraoss.co.jp>
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
@ 2026-03-26 23:07 ` Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 1 reply; 376+ messages in thread
From: Michael Paquier @ 2026-03-26 23:07 UTC (permalink / raw)
To: Yugo Nagata <nagata@sraoss.co.jp>; +Cc: Sami Imseih <samimseih@gmail.com>; pgsql-hackers
On Thu, Mar 26, 2026 at 07:22:03PM +0900, Yugo Nagata wrote:
> To handle the possibility that the table is dropped between
> RangeVarGetRelid() and the lock attempt, SearchSysCacheExists1() is
> used after acquiring the lock.
(Noticed while skimming through my emails this morning..)
+void
+pgstat_report_skipped_vacuum_analyze(Oid relid, bool vacuum, bool analyze,
+ bool autovacuum)
I'd recommend to replace this interface with three booleans with a set
of three bitwise flags. That would be less error prone for the
callers of this function, or we could finish by aggregating counters
we don't want to.
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, ../../acW8MW-Gb8Aj_TH2@paquier.xyz/2-signature.asc)
download
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
@ 2026-03-27 07:35 ` Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
0 siblings, 1 reply; 376+ messages in thread
From: Yugo Nagata @ 2026-03-27 07:35 UTC (permalink / raw)
To: Michael Paquier <michael@paquier.xyz>; +Cc: Sami Imseih <samimseih@gmail.com>; pgsql-hackers
On Fri, 27 Mar 2026 08:07:29 +0900
Michael Paquier <michael@paquier.xyz> wrote:
> On Thu, Mar 26, 2026 at 07:22:03PM +0900, Yugo Nagata wrote:
> > To handle the possibility that the table is dropped between
> > RangeVarGetRelid() and the lock attempt, SearchSysCacheExists1() is
> > used after acquiring the lock.
>
> (Noticed while skimming through my emails this morning..)
>
> +void
> +pgstat_report_skipped_vacuum_analyze(Oid relid, bool vacuum, bool analyze,
> + bool autovacuum)
>
> I'd recommend to replace this interface with three booleans with a set
> of three bitwise flags. That would be less error prone for the
> callers of this function, or we could finish by aggregating counters
> we don't want to.
Thank you for the suggestion.
I've attached a revised patch reflecting this change, and it also includes
the documentation.
Regards,
Yugo Nagata
--
Yugo Nagata <nagata@sraoss.co.jp>
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
@ 2026-03-27 16:48 ` Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 1 reply; 376+ messages in thread
From: Sami Imseih @ 2026-03-27 16:48 UTC (permalink / raw)
To: Yugo Nagata <nagata@sraoss.co.jp>; +Cc: Michael Paquier <michael@paquier.xyz>; pgsql-hackers
> I've attached a revised patch reflecting this change, and it also includes
> the documentation.
Thanks fo the update!
I have some comments:
1/
+pgstat_report_skipped_vacuum_analyze(Oid relid, bits8 flags)
using bit8 is fine here, but I would have just used int. For this
case, it's just a matter of prefernace.
2/
+/* flags for pgstat_flush_backend() */
+#define PGSTAT_REPORT_SKIPPED_VACUUM (1 << 0) /* vacuum is skipped */
+#define PGSTAT_REPORT_SKIPPED_ANALYZE (1 << 1) /* analyze is skipped */
+#define PGSTAT_REPORT_SKIPPED_AUTOVAC (1 << 2) /* skipped
during autovacuum/autoanalyze */
+#define PGSTAT_REPORT_SKIPPED_ANY (PGSTAT_REPORT_SKIPPED_VACUUM |
PGSTAT_REPORT_SKIPPED_ANALYZE)
can we just have 4 flags, SKIPPED_VACUUM, SKIPPED_ANALYZE,
SKIPPED_AUTOVACUUM, SKIPPED_AUTOANALYZE,
which can then remove the nested if/else and makes the mapping more obvious
+ if (flags & PGSTAT_REPORT_SKIPPED_AUTOVAC)
+ {
+ if (flags & PGSTAT_REPORT_SKIPPED_VACUUM)
+ {
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+ }
+ if (flags & PGSTAT_REPORT_SKIPPED_ANALYZE)
+ {
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+ }
+ }
+ else
+ {
+ if (flags & PGSTAT_REPORT_SKIPPED_VACUUM)
+ {
+ tabentry->last_skipped_vacuum_time = ts;
+ tabentry->skipped_vacuum_count++;
+ }
+ if (flags & PGSTAT_REPORT_SKIPPED_ANALYZE)
+ {
+ tabentry->last_skipped_analyze_time = ts;
+ tabentry->skipped_analyze_count++;
+ }
+ }
3/
For the sake of consistency, can we rename the fields from
skipped_vacuum_count to vacuum_skipped_count, etc. ? to be similar
to fields like vacuum_count
4/
field documentation could be a bit better to match existing phrasing
For example, the timestamp fields:
- Last time a manual vacuum on this table was attempted but skipped due to
- lock unavailability (not counting <command>VACUUM FULL</command>)
+ The time of the last manual vacuum on this table that was skipped
+ due to lock unavailability (not counting <command>VACUUM FULL</command>)
and the counter fields
- Number of times vacuums on this table have been attempted but skipped
+ Number of times a manual vacuum on this table has been skipped
5/
Partitioned table asymmetry between vacuum_count and vacuum_skipped_count.
vacuum_count never increments on a the parenttable, because the parent is never
pocessed. On the other hand, if the manual VACUUM/ANALYZE is on the
parent table,
then we will skip all the children. So, we should still report the skip on the
parent table, but we should add a Notes section in the docs perhaps to
document this caveat?
6/
It would be nice to add a test for this, but this requires concurrency and I'm
not sure it's woth it.
Also, can you create a CF entry in
https://commitfest.postgresql.org/59/, please.
Thanks!
--
Sami Imseih
Amazon Web Services (AWS)
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
@ 2026-03-28 07:18 ` Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 1 reply; 376+ messages in thread
From: Yugo Nagata @ 2026-03-28 07:18 UTC (permalink / raw)
To: Sami Imseih <samimseih@gmail.com>; +Cc: Michael Paquier <michael@paquier.xyz>; pgsql-hackers
On Fri, 27 Mar 2026 11:48:27 -0500
Sami Imseih <samimseih@gmail.com> wrote:
> > I've attached a revised patch reflecting this change, and it also includes
> > the documentation.
>
> Thanks fo the update!
>
> I have some comments:
>
> 1/
> +pgstat_report_skipped_vacuum_analyze(Oid relid, bits8 flags)
>
> using bit8 is fine here, but I would have just used int. For this
> case, it's just a matter of prefernace.
That makes sense, since using int for flags seems common in other
places in the code. I'm not sure how much it affects performance,
though.
> 2/
> +/* flags for pgstat_flush_backend() */
> +#define PGSTAT_REPORT_SKIPPED_VACUUM (1 << 0) /* vacuum is skipped */
> +#define PGSTAT_REPORT_SKIPPED_ANALYZE (1 << 1) /* analyze is skipped */
> +#define PGSTAT_REPORT_SKIPPED_AUTOVAC (1 << 2) /* skipped
> during autovacuum/autoanalyze */
> +#define PGSTAT_REPORT_SKIPPED_ANY (PGSTAT_REPORT_SKIPPED_VACUUM |
> PGSTAT_REPORT_SKIPPED_ANALYZE)
>
> can we just have 4 flags, SKIPPED_VACUUM, SKIPPED_ANALYZE,
> SKIPPED_AUTOVACUUM, SKIPPED_AUTOANALYZE,
> which can then remove the nested if/else and makes the mapping more obvious
I am fine with that. In that case, the nested logic would move to the
caller side.
> 3/
> For the sake of consistency, can we rename the fields from
>
> skipped_vacuum_count to vacuum_skipped_count, etc. ? to be similar
> to fields like vacuum_count
Hmm, I think skipped_vacuum_count is more consistent with
fields like last_vacuum and total_vacuum_time, where the modifier
comes before vacuum/analyze. What do you think about that?
> 4/
> field documentation could be a bit better to match existing phrasing
>
> For example, the timestamp fields:
>
> - Last time a manual vacuum on this table was attempted but skipped due to
> - lock unavailability (not counting <command>VACUUM FULL</command>)
> + The time of the last manual vacuum on this table that was skipped
> + due to lock unavailability (not counting <command>VACUUM FULL</command>)
I intended to keep consistency with the existing last_vacuum:
Last time at which this table was manually vacuumed (not counting VACUUM FULL)
although "at which" was accidentally omitted. Your suggestion seems
simpler and more natural to me. Should we prioritize that over consistency?
> and the counter fields
>
> - Number of times vacuums on this table have been attempted but skipped
> + Number of times a manual vacuum on this table has been skipped
The "a munual" was also accidentally omitted, so I'll fix it.
> 5/
> Partitioned table asymmetry between vacuum_count and vacuum_skipped_count.
>
> vacuum_count never increments on a the parenttable, because the parent is never
> pocessed. On the other hand, if the manual VACUUM/ANALYZE is on the
> parent table,
> then we will skip all the children. So, we should still report the skip on the
> parent table, but we should add a Notes section in the docs perhaps to
> document this caveat?
Yeah, we cannot report skips on the children when a manual
vacuum/analyze on the parent table is skipped. (It might be possible
to obtain child information with NoLock, but that would not be safe.)
Therefore, I agree that the best we can do here is to add a note to the
documentation of last_skipped_vacuum/analyze and skipped_vacuum/analyze_count.
For example:
When a manual vacuum or analyze on a parent table in an inheritance
or partitioning hierarchy is skipped, the statistics are recorded
only for the parent table, not for its children.
> 6/
> It would be nice to add a test for this, but this requires concurrency and I'm
> not sure it's woth it.
I'm not sure what meaningful tests we could add for these statistics.
I couldn't find any existing tests for fields like last_vacuum.
Regards,
Yugo Nagata
--
Yugo Nagata <nagata@sraoss.co.jp>
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
@ 2026-04-13 08:05 ` Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
0 siblings, 1 reply; 376+ messages in thread
From: Yugo Nagata @ 2026-04-13 08:05 UTC (permalink / raw)
To: Yugo Nagata <nagata@sraoss.co.jp>; +Cc: Sami Imseih <samimseih@gmail.com>; Michael Paquier <michael@paquier.xyz>; pgsql-hackers
Hello Sami Imseih,
On Sat, 28 Mar 2026 16:18:02 +0900
Yugo Nagata <nagata@sraoss.co.jp> wrote:
> On Fri, 27 Mar 2026 11:48:27 -0500
> Sami Imseih <samimseih@gmail.com> wrote:
>
> > > I've attached a revised patch reflecting this change, and it also includes
> > > the documentation.
> >
> > Thanks fo the update!
> >
> > I have some comments:
> >
> > 1/
> > +pgstat_report_skipped_vacuum_analyze(Oid relid, bits8 flags)
> >
> > using bit8 is fine here, but I would have just used int. For this
> > case, it's just a matter of prefernace.
>
> That makes sense, since using int for flags seems common in other
> places in the code. I'm not sure how much it affects performance,
> though.
>
> > 2/
> > +/* flags for pgstat_flush_backend() */
> > +#define PGSTAT_REPORT_SKIPPED_VACUUM (1 << 0) /* vacuum is skipped */
> > +#define PGSTAT_REPORT_SKIPPED_ANALYZE (1 << 1) /* analyze is skipped */
> > +#define PGSTAT_REPORT_SKIPPED_AUTOVAC (1 << 2) /* skipped
> > during autovacuum/autoanalyze */
> > +#define PGSTAT_REPORT_SKIPPED_ANY (PGSTAT_REPORT_SKIPPED_VACUUM |
> > PGSTAT_REPORT_SKIPPED_ANALYZE)
> >
> > can we just have 4 flags, SKIPPED_VACUUM, SKIPPED_ANALYZE,
> > SKIPPED_AUTOVACUUM, SKIPPED_AUTOANALYZE,
> > which can then remove the nested if/else and makes the mapping more obvious
>
> I am fine with that. In that case, the nested logic would move to the
> caller side.
>
> > 3/
> > For the sake of consistency, can we rename the fields from
> >
> > skipped_vacuum_count to vacuum_skipped_count, etc. ? to be similar
> > to fields like vacuum_count
>
> Hmm, I think skipped_vacuum_count is more consistent with
> fields like last_vacuum and total_vacuum_time, where the modifier
> comes before vacuum/analyze. What do you think about that?
>
> > 4/
> > field documentation could be a bit better to match existing phrasing
> >
> > For example, the timestamp fields:
> >
> > - Last time a manual vacuum on this table was attempted but skipped due to
> > - lock unavailability (not counting <command>VACUUM FULL</command>)
> > + The time of the last manual vacuum on this table that was skipped
> > + due to lock unavailability (not counting <command>VACUUM FULL</command>)
>
> I intended to keep consistency with the existing last_vacuum:
>
> Last time at which this table was manually vacuumed (not counting VACUUM FULL)
>
> although "at which" was accidentally omitted. Your suggestion seems
> simpler and more natural to me. Should we prioritize that over consistency?
>
> > and the counter fields
> >
> > - Number of times vacuums on this table have been attempted but skipped
> > + Number of times a manual vacuum on this table has been skipped
>
> The "a munual" was also accidentally omitted, so I'll fix it.
>
> > 5/
> > Partitioned table asymmetry between vacuum_count and vacuum_skipped_count.
> >
> > vacuum_count never increments on a the parenttable, because the parent is never
> > pocessed. On the other hand, if the manual VACUUM/ANALYZE is on the
> > parent table,
> > then we will skip all the children. So, we should still report the skip on the
> > parent table, but we should add a Notes section in the docs perhaps to
> > document this caveat?
>
> Yeah, we cannot report skips on the children when a manual
> vacuum/analyze on the parent table is skipped. (It might be possible
> to obtain child information with NoLock, but that would not be safe.)
>
> Therefore, I agree that the best we can do here is to add a note to the
> documentation of last_skipped_vacuum/analyze and skipped_vacuum/analyze_count.
>
> For example:
>
> When a manual vacuum or analyze on a parent table in an inheritance
> or partitioning hierarchy is skipped, the statistics are recorded
> only for the parent table, not for its children.
>
> > 6/
> > It would be nice to add a test for this, but this requires concurrency and I'm
> > not sure it's woth it.
>
> I'm not sure what meaningful tests we could add for these statistics.
> I couldn't find any existing tests for fields like last_vacuum.
I've attached a patch reflecting your comments on items 1, 2, and 5.
As for items 3, 4, and 6, I am waiting for your comments, so the patch
is left unchanged for now.
Regards,
Yugo Nagata
--
Yugo Nagata <nagata@sraoss.co.jp>
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
@ 2026-04-22 12:49 ` Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 1 reply; 376+ messages in thread
From: Sami Imseih @ 2026-04-22 12:49 UTC (permalink / raw)
To: Yugo Nagata <nagata@sraoss.co.jp>; +Cc: Michael Paquier <michael@paquier.xyz>; pgsql-hackers
Thanks for the updated patch!
> I've attached a patch reflecting your comments on items 1, 2, and 5.
> As for items 3, 4, and 6, I am waiting for your comments, so the patch
> is left unchanged for now.
A few more comments:
1/
+ relid = RangeVarGetRelid(vrel->relation, NoLock, false);
Should this be called with "true" as the 3rd (missing_ok) argument, otherwise
we end up with an error instead of a "--- relation no longer exists" log. right?
2/
Can the isolation tests
src/test/isolation/specs/vacuum-skip-locked.spec be updated
to check pg_stat_user_tables as well?
3/ comment fix:
This:
* Relation could not be opened hence generate if possible a log
Should be:
* Relation could not be opened, hence generate if possible a log
--
Sami Imseih
Amazon Web Services (AWS)
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
@ 2026-04-27 11:32 ` Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
0 siblings, 1 reply; 376+ messages in thread
From: Yugo Nagata @ 2026-04-27 11:32 UTC (permalink / raw)
To: Sami Imseih <samimseih@gmail.com>; +Cc: Michael Paquier <michael@paquier.xyz>; pgsql-hackers
On Wed, 22 Apr 2026 07:49:55 -0500
Sami Imseih <samimseih@gmail.com> wrote:
Thank you for your comments!
>
> 1/
>
> + relid = RangeVarGetRelid(vrel->relation, NoLock, false);
>
> Should this be called with "true" as the 3rd (missing_ok) argument, otherwise
> we end up with an error instead of a "--- relation no longer exists" log. right?
No, it should be false. If missing_ok is true, VACUUM (SKIP_LOCKED) on a not-existing
table would emit a "skipping vacuum of ... --- relation no longer exists" message, but
it should be "relation ... does not exist".
> 2/
>
> Can the isolation tests
> src/test/isolation/specs/vacuum-skip-locked.spec be updated
> to check pg_stat_user_tables as well?
Yes, we can. I've attached an updated patch including that test.
While working on the test, I noticed that skipped FULL VACUUM was counted
in the previous patch, so I fixed it not to avoid counting those cases.
> 3/ comment fix:
>
> This:
> * Relation could not be opened hence generate if possible a log
>
> Should be:
> * Relation could not be opened, hence generate if possible a log
Fixed.
The names of the new fields are still open. The current pattern is
"last_skipped_..." and "skipped_..._count". Alternatively, we could use
"..._last_skip" and "..._skip_count", which would be consistent with
slotsync_skip_count and slosync_last_skip.
Which do you think is better?
Regards,
Yugo Nagata
--
Yugo Nagata <nagata@sraoss.co.jp>
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
@ 2026-05-04 20:44 ` Sami Imseih <samimseih@gmail.com>
2026-05-12 09:47 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
0 siblings, 2 replies; 376+ messages in thread
From: Sami Imseih @ 2026-05-04 20:44 UTC (permalink / raw)
To: Yugo Nagata <nagata@sraoss.co.jp>; +Cc: Michael Paquier <michael@paquier.xyz>; pgsql-hackers
Thanks for the update!
> >
> > 1/
> >
> > + relid = RangeVarGetRelid(vrel->relation, NoLock, false);
> >
> > Should this be called with "true" as the 3rd (missing_ok) argument, otherwise
> > we end up with an error instead of a "--- relation no longer exists" log. right?
>
> No, it should be false. If missing_ok is true, VACUUM (SKIP_LOCKED) on a not-existing
> table would emit a "skipping vacuum of ... --- relation no longer exists" message, but
> it should be "relation ... does not exist".
Yeah you are right.
But, after looking more into this, I still think the
expand_vacuum_rel() changes can be
improved. The branching
- */
- if (!OidIsValid(relid))
+ if (!(options & VACOPT_SKIP_LOCKED))
{
- if (options & VACOPT_VACUUM)
- ereport(WARNING,
-
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
- errmsg("skipping
vacuum of \"%s\" --- lock not available",
-
vrel->relation->relname)));
- else
- ereport(WARNING,
-
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
- errmsg("skipping
analyze of \"%s\" --- lock not available",
+ relid = RangeVarGetRelidExtended(vrel->relation,
+
AccessShareLock,
+
0, NULL, NULL);
+ if (!OidIsValid(relid))
+ return vacrels;
+ }
+ else
+ {
+ /* Get relid for reporting before taking a lock */
+ relid = RangeVarGetRelid(vrel->relation, NoLock, false);
+
+ if (!ConditionalLockRelationOid(relid, AccessShareLock))
is not needed. We can continue just using RangeVarGetRelidExtended()
with the rvr_opts and an AccessExclusiveLock, and once we need to
report that we cannot obtain the lock, RangeVarGetRelid() can be
called at that point for the purpose of calling
pgstat_report_skipped_vacuum_analyze(). This is safer than calling
ConditionalLockRelationOid() on a relid obtained with NoLock. See
attached v6.
>> 2/
>>
>> Can the isolation tests
>> src/test/isolation/specs/vacuum-skip-locked.spec be updated
>> to check pg_stat_user_tables as well?
> Yes, we can. I've attached an updated patch including that test.
> While working on the test, I noticed that skipped FULL VACUUM was counted
> in the previous patch, so I fixed it not to avoid counting those cases.
The tests looks good to me.
> The names of the new fields are still open. The current pattern is
> "last_skipped_..." and "skipped_..._count". Alternatively, we could use
> "..._last_skip" and "..._skip_count", which would be consistent with
> slotsync_skip_count and slosync_last_skip.
> Which do you think is better?
I think last_skipped_* is better since we use last_vacuum, last_autovacuum, etc.
--
Sami
Attachments:
[application/octet-stream] v6-0001-Track-skipped-vacuum-and-analyze-activity-per-rel.patch (39.6K, ../../CAA5RZ0vwgXZ5kF2GvYBR+Ma1LPSbDjE9pjANzSiUw3wmpv51PQ@mail.gmail.com/2-v6-0001-Track-skipped-vacuum-and-analyze-activity-per-rel.patch)
download | inline diff:
From 13e2d6b8390ea48cb4fd3e604a46bde02b750bde Mon Sep 17 00:00:00 2001
From: Yugo Nagata <nagata@sraoss.co.jp>
Date: Tue, 24 Mar 2026 13:09:00 +0900
Subject: [PATCH v6 1/1] Track skipped vacuum and analyze activity per relation
This commit adds eight fields to the relation statistics that track
the last time vacuum or analyze has been attempted but skipped due to
lock unavailability, along with their counts:
- last_skipped_vacuum
- last_skipped_autovacuum
- last_skipped_analyze
- last_skipped_autoanalyze
- skipped_vacuum_count
- skipped_autovacuum_count
- skipped_analyze_count
- skipped_autoanalyze_count
These field can help users confirm that autovacuum is actively attempting
to run on a table that has not been vacuumed or analyzed for a long time,
and that the lack of progress is due to repeated skips rather than inactivity.
---
doc/src/sgml/monitoring.sgml | 88 ++++++
src/backend/catalog/system_views.sql | 8 +
src/backend/commands/vacuum.c | 31 +++
src/backend/utils/activity/pgstat_relation.c | 64 +++++
src/backend/utils/adt/pgstatfuncs.c | 24 ++
src/include/catalog/pg_proc.dat | 32 +++
src/include/pgstat.h | 17 ++
.../isolation/expected/vacuum-skip-locked.out | 260 ++++++++++++++++--
.../isolation/specs/vacuum-skip-locked.spec | 41 +--
src/test/regress/expected/rules.out | 24 ++
10 files changed, 555 insertions(+), 34 deletions(-)
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 08d5b824552..a9b579d87a9 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4387,6 +4387,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_skipped_vacuum</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a manual vacuum on this table was attempted but skipped due to
+ lock unavailability (not counting <command>VACUUM FULL</command>)
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_autovacuum</structfield> <type>timestamp with time zone</type>
@@ -4397,6 +4407,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_skipped_autovacuum</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a vacuum on this table by the autovacuum daemon was attempted
+ but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_analyze</structfield> <type>timestamp with time zone</type>
@@ -4406,6 +4426,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_skipped_analyze</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a manual analyze on this table was attempted but skipped due to
+ lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_autoanalyze</structfield> <type>timestamp with time zone</type>
@@ -4416,6 +4446,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_skipped_autoanalyze</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time at which an analyze on this table by the autovacuum was
+ attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>vacuum_count</structfield> <type>bigint</type>
@@ -4426,6 +4466,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>skipped_vacuum_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times manual vacuums on this table have been attempted but skipped
+ due to lock unavailability (not counting <command>VACUUM FULL</command>)
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>autovacuum_count</structfield> <type>bigint</type>
@@ -4436,6 +4486,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>skipped_autovacuum_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times vacuums on this table by the autovacuum daemon have been
+ attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>analyze_count</structfield> <type>bigint</type>
@@ -4445,6 +4505,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>skipped_analyze_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times manual analyzes on this table have been attempted but
+ skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>autoanalyze_count</structfield> <type>bigint</type>
@@ -4455,6 +4525,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>skipped_autoanalyze_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times analyzes on this table by the autovacuum daemon have
+ been attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>total_vacuum_time</structfield> <type>double precision</type>
@@ -4510,6 +4590,14 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</tgroup>
</table>
+ <note>
+ <para>
+ When a manual vacuum or analyze on a parent table in an inheritance or
+ partitioning hierarchy is skipped, the statistics are recorded only for
+ the parent table, not for its children.
+ </para>
+ </note>
+
</sect2>
<sect2 id="monitoring-pg-stat-autovacuum-scores-view">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 73a1c1c4670..f509fc7876b 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -736,13 +736,21 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze,
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
+ pg_stat_get_last_skipped_vacuum_time(C.oid) as last_skipped_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
+ pg_stat_get_last_skipped_analyze_time(C.oid) as last_skipped_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
+ pg_stat_get_skipped_vacuum_count(C.oid) AS skipped_vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
+ pg_stat_get_skipped_analyze_count(C.oid) AS skipped_analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 99d0db82ed7..51c5fc2fdaf 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -793,8 +793,26 @@ vacuum_open_relation(Oid relid, RangeVar *relation, uint32 options,
rel = try_relation_open(relid, NoLock);
else
{
+ int flags = 0;
+
rel = NULL;
rel_lock = false;
+
+ if ((options & VACOPT_VACUUM) != 0 && (options & VACOPT_FULL) == 0)
+ {
+ if (AmAutoVacuumWorkerProcess())
+ flags |= PGSTAT_REPORT_SKIPPED_AUTOVACUUM;
+ else
+ flags |= PGSTAT_REPORT_SKIPPED_VACUUM;
+ }
+ if ((options & VACOPT_ANALYZE) != 0)
+ {
+ if (AmAutoVacuumWorkerProcess())
+ flags |= PGSTAT_REPORT_SKIPPED_AUTOANALYZE;
+ else
+ flags |= PGSTAT_REPORT_SKIPPED_ANALYZE;
+ }
+ pgstat_report_skipped_vacuum_analyze(relid, flags);
}
/* if relation is opened, leave */
@@ -930,6 +948,8 @@ expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context,
*/
if (!OidIsValid(relid))
{
+ int flags = 0;
+
if (options & VACOPT_VACUUM)
ereport(WARNING,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
@@ -940,6 +960,17 @@ expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
vrel->relation->relname)));
+
+ /* Get relid for statistics reporting */
+ relid = RangeVarGetRelid(vrel->relation, NoLock, true);
+
+ if ((options & VACOPT_VACUUM) != 0 && (options & VACOPT_FULL) == 0)
+ flags |= PGSTAT_REPORT_SKIPPED_VACUUM;
+ if ((options & VACOPT_ANALYZE) != 0)
+ flags |= PGSTAT_REPORT_SKIPPED_ANALYZE;
+
+ pgstat_report_skipped_vacuum_analyze(relid, flags);
+
return vacrels;
}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index b2ca28f83ba..21d1b382ba0 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -17,12 +17,14 @@
#include "postgres.h"
+#include "access/htup_details.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "utils/memutils.h"
#include "utils/pgstat_internal.h"
#include "utils/rel.h"
+#include "utils/syscache.h"
#include "utils/timestamp.h"
@@ -367,6 +369,68 @@ pgstat_report_analyze(Relation rel,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during vacuum or/and analyze.
+ */
+void
+pgstat_report_skipped_vacuum_analyze(Oid relid, int flags)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+ HeapTuple classTup;
+ bool isshared;
+
+ if (!pgstat_track_counts || !flags)
+ return;
+
+ classTup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(classTup))
+ return; /* somebody deleted the rel, forget it */
+ isshared = ((Form_pg_class) GETSTRUCT(classTup))->relisshared;
+ ReleaseSysCache(classTup);
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
+ isshared ? InvalidOid : MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+ if (flags & PGSTAT_REPORT_SKIPPED_VACUUM)
+ {
+ tabentry->last_skipped_vacuum_time = ts;
+ tabentry->skipped_vacuum_count++;
+ }
+ else if (flags & PGSTAT_REPORT_SKIPPED_AUTOVACUUM)
+ {
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+ }
+
+ if (flags & PGSTAT_REPORT_SKIPPED_ANALYZE)
+ {
+ tabentry->last_skipped_analyze_time = ts;
+ tabentry->skipped_analyze_count++;
+ }
+ else if (flags & PGSTAT_REPORT_SKIPPED_AUTOANALYZE)
+ {
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+ }
+
+ pgstat_unlock_entry(entry_ref);
+
+ /* see pgstat_report_vacuum() */
+ pgstat_flush_io(false);
+ (void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
+}
+
/*
* count a tuple insertion of n tuples
*/
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 7a9dfa9ba3b..aef291217b6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,18 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_analyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_analyze_count)
+
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
+/* pg_stat_get_skipped_vacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_vacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +182,18 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_analyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_analyze_time)
+
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
+/* pg_stat_get_last_skipped_vacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_vacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index fa9ae79082b..32debb34863 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5680,6 +5680,38 @@
proargmodes => '{o,o,o,o,o,o,o,o,o,o}',
proargnames => '{oid,score,xid_score,mxid_score,vacuum_score,vacuum_insert_score,analyze_score,do_vacuum,do_analyze,for_wraparound}',
prosrc => 'pg_stat_get_autovacuum_scores' },
+{ oid => '8142', descr => 'statistics: last skipped vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_vacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_vacuum_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: last skipped analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_analyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_analyze_time' },
+{ oid => '8145', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8146', descr => 'statistics: number of skipped vacuum for a table',
+ proname => 'pg_stat_get_skipped_vacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_vacuum_count' },
+{ oid => '8147', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
+{ oid => '8148', descr => 'statistics: number of skipped analyzes for a table',
+ proname => 'pg_stat_get_skipped_analyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_analyze_count' },
+{ oid => '8149', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index dfa2e837638..54b9fc60bc3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -479,6 +479,15 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_vacuum_time; /* user initiated vacuum */
+ PgStat_Counter skipped_vacuum_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_analyze_time; /* user initiated */
+ PgStat_Counter skipped_analyze_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -703,6 +712,14 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+/* flags for pgstat_flush_backend() */
+#define PGSTAT_REPORT_SKIPPED_VACUUM (1 << 0) /* vacuum is skipped */
+#define PGSTAT_REPORT_SKIPPED_ANALYZE (1 << 1) /* analyze is skipped */
+#define PGSTAT_REPORT_SKIPPED_AUTOVACUUM (1 << 2) /* autovacuum is skipped */
+#define PGSTAT_REPORT_SKIPPED_AUTOANALYZE (1 << 3) /* autoanalyze is
+ * skipped */
+extern void pgstat_report_skipped_vacuum_analyze(Oid relid, int flags);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/isolation/expected/vacuum-skip-locked.out b/src/test/isolation/expected/vacuum-skip-locked.out
index 99db281a159..e2cff175b7c 100644
--- a/src/test/isolation/expected/vacuum-skip-locked.out
+++ b/src/test/isolation/expected/vacuum-skip-locked.out
@@ -1,6 +1,6 @@
Parsed test spec with 2 sessions
-starting permutation: lock_share vac_specified commit
+starting permutation: lock_share vac_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -10,8 +10,22 @@ step vac_specified: VACUUM (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -20,8 +34,22 @@ step vac_all_parts: VACUUM (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
+
-starting permutation: lock_share analyze_specified commit
+starting permutation: lock_share analyze_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -31,8 +59,22 @@ step analyze_specified: ANALYZE (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share analyze_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_share analyze_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -41,8 +83,22 @@ step analyze_all_parts: ANALYZE (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 1| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_share vac_analyze_specified commit
+starting permutation: lock_share vac_analyze_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -52,8 +108,22 @@ step vac_analyze_specified: VACUUM (ANALYZE, SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_analyze_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_analyze_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -62,8 +132,22 @@ step vac_analyze_all_parts: VACUUM (ANALYZE, SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 1| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_share vac_full_specified commit
+starting permutation: lock_share vac_full_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -73,8 +157,22 @@ step vac_full_specified: VACUUM (SKIP_LOCKED, FULL) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_full_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_full_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -83,8 +181,22 @@ step vac_full_all_parts: VACUUM (SKIP_LOCKED, FULL) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_specified commit
+
+starting permutation: lock_access_exclusive vac_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -94,8 +206,22 @@ step vac_specified: VACUUM (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_all_parts commit
+
+starting permutation: lock_access_exclusive vac_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -104,8 +230,22 @@ step vac_all_parts: VACUUM (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive analyze_specified commit
+
+starting permutation: lock_access_exclusive analyze_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -115,8 +255,22 @@ step analyze_specified: ANALYZE (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_access_exclusive analyze_all_parts commit
+starting permutation: lock_access_exclusive analyze_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -126,8 +280,22 @@ step commit:
COMMIT;
step analyze_all_parts: <... completed>
-
-starting permutation: lock_access_exclusive vac_analyze_specified commit
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 1| 0
+part1 | 0| 0| 1| 0
+part2 | 0| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_analyze_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -137,8 +305,22 @@ step vac_analyze_specified: VACUUM (ANALYZE, SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_analyze_all_parts commit
+
+starting permutation: lock_access_exclusive vac_analyze_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -148,8 +330,22 @@ step commit:
COMMIT;
step vac_analyze_all_parts: <... completed>
-
-starting permutation: lock_access_exclusive vac_full_specified commit
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 1| 0
+part1 | 1| 0| 1| 0
+part2 | 1| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_full_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -159,8 +355,22 @@ step vac_full_specified: VACUUM (SKIP_LOCKED, FULL) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_access_exclusive vac_full_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_full_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -169,3 +379,17 @@ step vac_full_all_parts: VACUUM (SKIP_LOCKED, FULL) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
diff --git a/src/test/isolation/specs/vacuum-skip-locked.spec b/src/test/isolation/specs/vacuum-skip-locked.spec
index 3fad6e1c92a..b0da75d4b6d 100644
--- a/src/test/isolation/specs/vacuum-skip-locked.spec
+++ b/src/test/isolation/specs/vacuum-skip-locked.spec
@@ -33,6 +33,15 @@ step commit
COMMIT;
}
+step check_stat
+{
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+}
+
session s2
step vac_specified { VACUUM (SKIP_LOCKED) part1, part2; }
step vac_all_parts { VACUUM (SKIP_LOCKED) parted; }
@@ -43,19 +52,19 @@ step vac_analyze_all_parts { VACUUM (ANALYZE, SKIP_LOCKED) parted; }
step vac_full_specified { VACUUM (SKIP_LOCKED, FULL) part1, part2; }
step vac_full_all_parts { VACUUM (SKIP_LOCKED, FULL) parted; }
-permutation lock_share vac_specified commit
-permutation lock_share vac_all_parts commit
-permutation lock_share analyze_specified commit
-permutation lock_share analyze_all_parts commit
-permutation lock_share vac_analyze_specified commit
-permutation lock_share vac_analyze_all_parts commit
-permutation lock_share vac_full_specified commit
-permutation lock_share vac_full_all_parts commit
-permutation lock_access_exclusive vac_specified commit
-permutation lock_access_exclusive vac_all_parts commit
-permutation lock_access_exclusive analyze_specified commit
-permutation lock_access_exclusive analyze_all_parts commit
-permutation lock_access_exclusive vac_analyze_specified commit
-permutation lock_access_exclusive vac_analyze_all_parts commit
-permutation lock_access_exclusive vac_full_specified commit
-permutation lock_access_exclusive vac_full_all_parts commit
+permutation lock_share vac_specified commit check_stat
+permutation lock_share vac_all_parts commit check_stat
+permutation lock_share analyze_specified commit check_stat
+permutation lock_share analyze_all_parts commit check_stat
+permutation lock_share vac_analyze_specified commit check_stat
+permutation lock_share vac_analyze_all_parts commit check_stat
+permutation lock_share vac_full_specified commit check_stat
+permutation lock_share vac_full_all_parts commit check_stat
+permutation lock_access_exclusive vac_specified commit check_stat
+permutation lock_access_exclusive vac_all_parts commit check_stat
+permutation lock_access_exclusive analyze_specified commit check_stat
+permutation lock_access_exclusive analyze_all_parts commit check_stat
+permutation lock_access_exclusive vac_analyze_specified commit check_stat
+permutation lock_access_exclusive vac_analyze_all_parts commit check_stat
+permutation lock_access_exclusive vac_full_specified commit check_stat
+permutation lock_access_exclusive vac_full_all_parts commit check_stat
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index a65a5bf0c4f..9b2075d3373 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1835,13 +1835,21 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
+ pg_stat_get_last_skipped_vacuum_time(c.oid) AS last_skipped_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
+ pg_stat_get_last_skipped_analyze_time(c.oid) AS last_skipped_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
+ pg_stat_get_skipped_vacuum_count(c.oid) AS skipped_vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
+ pg_stat_get_skipped_analyze_count(c.oid) AS skipped_analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2346,13 +2354,21 @@ pg_stat_sys_tables| SELECT relid,
n_mod_since_analyze,
n_ins_since_vacuum,
last_vacuum,
+ last_skipped_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
+ last_skipped_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
+ skipped_vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
+ skipped_analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2401,13 +2417,21 @@ pg_stat_user_tables| SELECT relid,
n_mod_since_analyze,
n_ins_since_vacuum,
last_vacuum,
+ last_skipped_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
+ last_skipped_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
+ skipped_vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
+ skipped_analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
@ 2026-05-12 09:47 ` Yugo Nagata <nagata@sraoss.co.jp>
2026-05-12 14:50 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
1 sibling, 1 reply; 376+ messages in thread
From: Yugo Nagata @ 2026-05-12 09:47 UTC (permalink / raw)
To: Sami Imseih <samimseih@gmail.com>; +Cc: Michael Paquier <michael@paquier.xyz>; pgsql-hackers
On Mon, 4 May 2026 15:44:57 -0500
Sami Imseih <samimseih@gmail.com> wrote:
Thank you for your review!
> > > 1/
> > >
> > > + relid = RangeVarGetRelid(vrel->relation, NoLock, false);
> > >
> > > Should this be called with "true" as the 3rd (missing_ok) argument, otherwise
> > > we end up with an error instead of a "--- relation no longer exists" log. right?
> >
> > No, it should be false. If missing_ok is true, VACUUM (SKIP_LOCKED) on a not-existing
> > table would emit a "skipping vacuum of ... --- relation no longer exists" message, but
> > it should be "relation ... does not exist".
>
> Yeah you are right.
>
> But, after looking more into this, I still think the
> expand_vacuum_rel() changes can be
> improved. The branching
> - */
> - if (!OidIsValid(relid))
> + if (!(options & VACOPT_SKIP_LOCKED))
> {
> - if (options & VACOPT_VACUUM)
> - ereport(WARNING,
> -
> (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
> - errmsg("skipping
> vacuum of \"%s\" --- lock not available",
> -
> vrel->relation->relname)));
> - else
> - ereport(WARNING,
> -
> (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
> - errmsg("skipping
> analyze of \"%s\" --- lock not available",
> + relid = RangeVarGetRelidExtended(vrel->relation,
> +
> AccessShareLock,
> +
> 0, NULL, NULL);
> + if (!OidIsValid(relid))
> + return vacrels;
> + }
> + else
> + {
> + /* Get relid for reporting before taking a lock */
> + relid = RangeVarGetRelid(vrel->relation, NoLock, false);
> +
> + if (!ConditionalLockRelationOid(relid, AccessShareLock))
>
> is not needed. We can continue just using RangeVarGetRelidExtended()
> with the rvr_opts and an AccessExclusiveLock, and once we need to
> report that we cannot obtain the lock, RangeVarGetRelid() can be
> called at that point for the purpose of calling
> pgstat_report_skipped_vacuum_analyze(). This is safer than calling
> ConditionalLockRelationOid() on a relid obtained with NoLock. See
> attached v6.
It seems good to me.
Initially, I was concerned that something might go wrong if a concurrent
session performed DROP TABLE or ALTER TABLE RENAME between RangeVarGetRelidExtended()
and RangeVarGetRelid(), but I could not find any actual issue. Even when the table
name is changed, the correct statistics entry is updated correctly.
So I'm fine with your version.
Regards,
Yugo Nagata
> >> 2/
> >>
> >> Can the isolation tests
> >> src/test/isolation/specs/vacuum-skip-locked.spec be updated
> >> to check pg_stat_user_tables as well?
>
> > Yes, we can. I've attached an updated patch including that test.
>
> > While working on the test, I noticed that skipped FULL VACUUM was counted
> > in the previous patch, so I fixed it not to avoid counting those cases.
>
> The tests looks good to me.
>
> > The names of the new fields are still open. The current pattern is
> > "last_skipped_..." and "skipped_..._count". Alternatively, we could use
> > "..._last_skip" and "..._skip_count", which would be consistent with
> > slotsync_skip_count and slosync_last_skip.
>
> > Which do you think is better?
>
> I think last_skipped_* is better since we use last_vacuum, last_autovacuum, etc.
>
> --
> Sami
--
Yugo Nagata <nagata@sraoss.co.jp>
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-12 09:47 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
@ 2026-05-12 14:50 ` Sami Imseih <samimseih@gmail.com>
0 siblings, 0 replies; 376+ messages in thread
From: Sami Imseih @ 2026-05-12 14:50 UTC (permalink / raw)
To: Yugo Nagata <nagata@sraoss.co.jp>; +Cc: Michael Paquier <michael@paquier.xyz>; pgsql-hackers
> So I'm fine with your version.
Thanks!
I marked the CF entry as RFC.
--
Sami
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
@ 2026-05-14 21:46 ` Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
1 sibling, 1 reply; 376+ messages in thread
From: Zsolt Parragi @ 2026-05-14 21:46 UTC (permalink / raw)
To: Sami Imseih <samimseih@gmail.com>; +Cc: Yugo Nagata <nagata@sraoss.co.jp>; Michael Paquier <michael@paquier.xyz>; pgsql-hackers
Hello!
+ TimestampTz last_skipped_vacuum_time; /* user initiated vacuum */
+ PgStat_Counter skipped_vacuum_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_analyze_time; /* user initiated */
+ PgStat_Counter skipped_analyze_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
Doesn't these also require a PGSTAT_FILE_FORMAT_ID change?
There's also an asymmetric case for the skipped counters, is that intentional?
| Command | `skipped_vacuum_count` |
`skipped_analyze_count` |
|-----------------------------------------|------------------------|-------------------------|
| `VACUUM (FULL, ANALYZE, SKIP_LOCKED) t` | 0 | 1
|
| `VACUUM (ANALYZE, SKIP_LOCKED) t` | 1 | 1
|
| `VACUUM (FULL, SKIP_LOCKED) t` | 0 | 0
|
> Initially, I was concerned that something might go wrong if a concurrent
> session performed DROP TABLE or ALTER TABLE RENAME between RangeVarGetRelidExtended()
> and RangeVarGetRelid(), but I could not find any actual issue. Even when the table
> name is changed, the correct statistics entry is updated correctly.
A DROP TABLE can cause a missed skip in statistics, which is
reproducible with a custom injection point and tap test, see the
attached patch. The race window is quite minimal, but it exists.
Attachments:
[application/octet-stream] 0001-DROP-TABLE-race-in-expand_vacuum_rel-skip-lock-path.patch.nocfbot (3.1K, ../../CAN4CZFO5BAr2uP=2CDDyX5ydFw8Ku=wtJ0ZtfLTKuWvXH6J2yQ@mail.gmail.com/2-0001-DROP-TABLE-race-in-expand_vacuum_rel-skip-lock-path.patch.nocfbot)
download
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
@ 2026-05-15 00:59 ` Sami Imseih <samimseih@gmail.com>
2026-05-15 05:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 1 reply; 376+ messages in thread
From: Sami Imseih @ 2026-05-15 00:59 UTC (permalink / raw)
To: Zsolt Parragi <zsolt.parragi@percona.com>; +Cc: Yugo Nagata <nagata@sraoss.co.jp>; Michael Paquier <michael@paquier.xyz>; pgsql-hackers
> Doesn't these also require a PGSTAT_FILE_FORMAT_ID change?
right. that was missed. Fixed in the attached.
> There's also an asymmetric case for the skipped counters, is that intentional?
>
> | Command | `skipped_vacuum_count` |
> `skipped_analyze_count` |
> |-----------------------------------------|------------------------|-------------------------|
> | `VACUUM (FULL, ANALYZE, SKIP_LOCKED) t` | 0 | 1
> |
> | `VACUUM (ANALYZE, SKIP_LOCKED) t` | 1 | 1
> |
> | `VACUUM (FULL, SKIP_LOCKED) t` | 0 | 0
Yeah, this is because vacuum_count and last_vacuum also skip VACUUM FULL.
That was mentioned earlier in the thread.
> > Initially, I was concerned that something might go wrong if a concurrent
> > session performed DROP TABLE or ALTER TABLE RENAME between RangeVarGetRelidExtended()
> > and RangeVarGetRelid(), but I could not find any actual issue. Even when the table
> > name is changed, the correct statistics entry is updated correctly.
>
> A DROP TABLE can cause a missed skip in statistics, which is
> reproducible with a custom injection point and tap test, see the
> attached patch. The race window is quite minimal, but it exists.
If the table is dropped, there are no stats to update. right?
--
Sami
Attachments:
[application/octet-stream] v7-0001-Track-skipped-vacuum-and-analyze-activity-per-rel.patch (39.8K, ../../CAA5RZ0v-xJ5fdTvNOdfokbY10pjjP=x_sfQ6Td30bbooomysaw@mail.gmail.com/2-v7-0001-Track-skipped-vacuum-and-analyze-activity-per-rel.patch)
download | inline diff:
From 6f5294f1c09edf3f5cd11d9732d49ef74061f963 Mon Sep 17 00:00:00 2001
From: Yugo Nagata <nagata@sraoss.co.jp>
Date: Tue, 24 Mar 2026 13:09:00 +0900
Subject: [PATCH v7 1/1] Track skipped vacuum and analyze activity per relation
This commit adds eight fields to the relation statistics that track
the last time vacuum or analyze has been attempted but skipped due to
lock unavailability, along with their counts:
- last_skipped_vacuum
- last_skipped_autovacuum
- last_skipped_analyze
- last_skipped_autoanalyze
- skipped_vacuum_count
- skipped_autovacuum_count
- skipped_analyze_count
- skipped_autoanalyze_count
These field can help users confirm that autovacuum is actively attempting
to run on a table that has not been vacuumed or analyzed for a long time,
and that the lack of progress is due to repeated skips rather than inactivity.
---
doc/src/sgml/monitoring.sgml | 88 ++++++
src/backend/catalog/system_views.sql | 8 +
src/backend/commands/vacuum.c | 31 +++
src/backend/utils/activity/pgstat_relation.c | 64 +++++
src/backend/utils/adt/pgstatfuncs.c | 24 ++
src/include/catalog/pg_proc.dat | 32 +++
src/include/pgstat.h | 19 +-
.../isolation/expected/vacuum-skip-locked.out | 260 ++++++++++++++++--
.../isolation/specs/vacuum-skip-locked.spec | 41 +--
src/test/regress/expected/rules.out | 24 ++
10 files changed, 556 insertions(+), 35 deletions(-)
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 08d5b824552..a9b579d87a9 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4387,6 +4387,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_skipped_vacuum</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a manual vacuum on this table was attempted but skipped due to
+ lock unavailability (not counting <command>VACUUM FULL</command>)
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_autovacuum</structfield> <type>timestamp with time zone</type>
@@ -4397,6 +4407,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_skipped_autovacuum</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a vacuum on this table by the autovacuum daemon was attempted
+ but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_analyze</structfield> <type>timestamp with time zone</type>
@@ -4406,6 +4426,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_skipped_analyze</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a manual analyze on this table was attempted but skipped due to
+ lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_autoanalyze</structfield> <type>timestamp with time zone</type>
@@ -4416,6 +4446,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_skipped_autoanalyze</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time at which an analyze on this table by the autovacuum was
+ attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>vacuum_count</structfield> <type>bigint</type>
@@ -4426,6 +4466,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>skipped_vacuum_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times manual vacuums on this table have been attempted but skipped
+ due to lock unavailability (not counting <command>VACUUM FULL</command>)
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>autovacuum_count</structfield> <type>bigint</type>
@@ -4436,6 +4486,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>skipped_autovacuum_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times vacuums on this table by the autovacuum daemon have been
+ attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>analyze_count</structfield> <type>bigint</type>
@@ -4445,6 +4505,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>skipped_analyze_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times manual analyzes on this table have been attempted but
+ skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>autoanalyze_count</structfield> <type>bigint</type>
@@ -4455,6 +4525,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>skipped_autoanalyze_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times analyzes on this table by the autovacuum daemon have
+ been attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>total_vacuum_time</structfield> <type>double precision</type>
@@ -4510,6 +4590,14 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</tgroup>
</table>
+ <note>
+ <para>
+ When a manual vacuum or analyze on a parent table in an inheritance or
+ partitioning hierarchy is skipped, the statistics are recorded only for
+ the parent table, not for its children.
+ </para>
+ </note>
+
</sect2>
<sect2 id="monitoring-pg-stat-autovacuum-scores-view">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 73a1c1c4670..f509fc7876b 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -736,13 +736,21 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze,
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
+ pg_stat_get_last_skipped_vacuum_time(C.oid) as last_skipped_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
+ pg_stat_get_last_skipped_analyze_time(C.oid) as last_skipped_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
+ pg_stat_get_skipped_vacuum_count(C.oid) AS skipped_vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
+ pg_stat_get_skipped_analyze_count(C.oid) AS skipped_analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index a4abb29cf64..019cfa03679 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -793,8 +793,26 @@ vacuum_open_relation(Oid relid, RangeVar *relation, uint32 options,
rel = try_relation_open(relid, NoLock);
else
{
+ int flags = 0;
+
rel = NULL;
rel_lock = false;
+
+ if ((options & VACOPT_VACUUM) != 0 && (options & VACOPT_FULL) == 0)
+ {
+ if (AmAutoVacuumWorkerProcess())
+ flags |= PGSTAT_REPORT_SKIPPED_AUTOVACUUM;
+ else
+ flags |= PGSTAT_REPORT_SKIPPED_VACUUM;
+ }
+ if ((options & VACOPT_ANALYZE) != 0)
+ {
+ if (AmAutoVacuumWorkerProcess())
+ flags |= PGSTAT_REPORT_SKIPPED_AUTOANALYZE;
+ else
+ flags |= PGSTAT_REPORT_SKIPPED_ANALYZE;
+ }
+ pgstat_report_skipped_vacuum_analyze(relid, flags);
}
/* if relation is opened, leave */
@@ -930,6 +948,8 @@ expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context,
*/
if (!OidIsValid(relid))
{
+ int flags = 0;
+
if (options & VACOPT_VACUUM)
ereport(WARNING,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
@@ -940,6 +960,17 @@ expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
vrel->relation->relname)));
+
+ /* Get relid for statistics reporting */
+ relid = RangeVarGetRelid(vrel->relation, NoLock, true);
+
+ if ((options & VACOPT_VACUUM) != 0 && (options & VACOPT_FULL) == 0)
+ flags |= PGSTAT_REPORT_SKIPPED_VACUUM;
+ if ((options & VACOPT_ANALYZE) != 0)
+ flags |= PGSTAT_REPORT_SKIPPED_ANALYZE;
+
+ pgstat_report_skipped_vacuum_analyze(relid, flags);
+
return vacrels;
}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index b2ca28f83ba..21d1b382ba0 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -17,12 +17,14 @@
#include "postgres.h"
+#include "access/htup_details.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "utils/memutils.h"
#include "utils/pgstat_internal.h"
#include "utils/rel.h"
+#include "utils/syscache.h"
#include "utils/timestamp.h"
@@ -367,6 +369,68 @@ pgstat_report_analyze(Relation rel,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during vacuum or/and analyze.
+ */
+void
+pgstat_report_skipped_vacuum_analyze(Oid relid, int flags)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+ HeapTuple classTup;
+ bool isshared;
+
+ if (!pgstat_track_counts || !flags)
+ return;
+
+ classTup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(classTup))
+ return; /* somebody deleted the rel, forget it */
+ isshared = ((Form_pg_class) GETSTRUCT(classTup))->relisshared;
+ ReleaseSysCache(classTup);
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
+ isshared ? InvalidOid : MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+ if (flags & PGSTAT_REPORT_SKIPPED_VACUUM)
+ {
+ tabentry->last_skipped_vacuum_time = ts;
+ tabentry->skipped_vacuum_count++;
+ }
+ else if (flags & PGSTAT_REPORT_SKIPPED_AUTOVACUUM)
+ {
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+ }
+
+ if (flags & PGSTAT_REPORT_SKIPPED_ANALYZE)
+ {
+ tabentry->last_skipped_analyze_time = ts;
+ tabentry->skipped_analyze_count++;
+ }
+ else if (flags & PGSTAT_REPORT_SKIPPED_AUTOANALYZE)
+ {
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+ }
+
+ pgstat_unlock_entry(entry_ref);
+
+ /* see pgstat_report_vacuum() */
+ pgstat_flush_io(false);
+ (void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
+}
+
/*
* count a tuple insertion of n tuples
*/
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 6f9c9c72de5..c45126f18c3 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,18 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_analyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_analyze_count)
+
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
+/* pg_stat_get_skipped_vacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_vacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +182,18 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_analyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_analyze_time)
+
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
+/* pg_stat_get_last_skipped_vacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_vacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index be157a5fbe9..d72921a1626 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5681,6 +5681,38 @@
proargmodes => '{o,o,o,o,o,o,o,o,o,o}',
proargnames => '{oid,score,xid_score,mxid_score,vacuum_score,vacuum_insert_score,analyze_score,do_vacuum,do_analyze,for_wraparound}',
prosrc => 'pg_stat_get_autovacuum_scores' },
+{ oid => '8142', descr => 'statistics: last skipped vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_vacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_vacuum_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: last skipped analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_analyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_analyze_time' },
+{ oid => '8145', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8146', descr => 'statistics: number of skipped vacuum for a table',
+ proname => 'pg_stat_get_skipped_vacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_vacuum_count' },
+{ oid => '8147', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
+{ oid => '8148', descr => 'statistics: number of skipped analyzes for a table',
+ proname => 'pg_stat_get_skipped_analyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_analyze_count' },
+{ oid => '8149', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index dfa2e837638..e31871ed0cc 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -218,7 +218,7 @@ typedef struct PgStat_TableXactStatus
* ------------------------------------------------------------
*/
-#define PGSTAT_FILE_FORMAT_ID 0x01A5BCBC
+#define PGSTAT_FILE_FORMAT_ID 0x01A5BCBD
typedef struct PgStat_ArchiverStats
{
@@ -479,6 +479,15 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_vacuum_time; /* user initiated vacuum */
+ PgStat_Counter skipped_vacuum_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_analyze_time; /* user initiated */
+ PgStat_Counter skipped_analyze_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -703,6 +712,14 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+/* flags for pgstat_flush_backend() */
+#define PGSTAT_REPORT_SKIPPED_VACUUM (1 << 0) /* vacuum is skipped */
+#define PGSTAT_REPORT_SKIPPED_ANALYZE (1 << 1) /* analyze is skipped */
+#define PGSTAT_REPORT_SKIPPED_AUTOVACUUM (1 << 2) /* autovacuum is skipped */
+#define PGSTAT_REPORT_SKIPPED_AUTOANALYZE (1 << 3) /* autoanalyze is
+ * skipped */
+extern void pgstat_report_skipped_vacuum_analyze(Oid relid, int flags);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/isolation/expected/vacuum-skip-locked.out b/src/test/isolation/expected/vacuum-skip-locked.out
index 99db281a159..e2cff175b7c 100644
--- a/src/test/isolation/expected/vacuum-skip-locked.out
+++ b/src/test/isolation/expected/vacuum-skip-locked.out
@@ -1,6 +1,6 @@
Parsed test spec with 2 sessions
-starting permutation: lock_share vac_specified commit
+starting permutation: lock_share vac_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -10,8 +10,22 @@ step vac_specified: VACUUM (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -20,8 +34,22 @@ step vac_all_parts: VACUUM (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
+
-starting permutation: lock_share analyze_specified commit
+starting permutation: lock_share analyze_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -31,8 +59,22 @@ step analyze_specified: ANALYZE (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share analyze_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_share analyze_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -41,8 +83,22 @@ step analyze_all_parts: ANALYZE (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 1| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_share vac_analyze_specified commit
+starting permutation: lock_share vac_analyze_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -52,8 +108,22 @@ step vac_analyze_specified: VACUUM (ANALYZE, SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_analyze_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_analyze_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -62,8 +132,22 @@ step vac_analyze_all_parts: VACUUM (ANALYZE, SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 1| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_share vac_full_specified commit
+starting permutation: lock_share vac_full_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -73,8 +157,22 @@ step vac_full_specified: VACUUM (SKIP_LOCKED, FULL) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_full_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_full_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -83,8 +181,22 @@ step vac_full_all_parts: VACUUM (SKIP_LOCKED, FULL) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_specified commit
+
+starting permutation: lock_access_exclusive vac_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -94,8 +206,22 @@ step vac_specified: VACUUM (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_all_parts commit
+
+starting permutation: lock_access_exclusive vac_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -104,8 +230,22 @@ step vac_all_parts: VACUUM (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive analyze_specified commit
+
+starting permutation: lock_access_exclusive analyze_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -115,8 +255,22 @@ step analyze_specified: ANALYZE (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_access_exclusive analyze_all_parts commit
+starting permutation: lock_access_exclusive analyze_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -126,8 +280,22 @@ step commit:
COMMIT;
step analyze_all_parts: <... completed>
-
-starting permutation: lock_access_exclusive vac_analyze_specified commit
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 1| 0
+part1 | 0| 0| 1| 0
+part2 | 0| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_analyze_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -137,8 +305,22 @@ step vac_analyze_specified: VACUUM (ANALYZE, SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_analyze_all_parts commit
+
+starting permutation: lock_access_exclusive vac_analyze_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -148,8 +330,22 @@ step commit:
COMMIT;
step vac_analyze_all_parts: <... completed>
-
-starting permutation: lock_access_exclusive vac_full_specified commit
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 1| 0
+part1 | 1| 0| 1| 0
+part2 | 1| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_full_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -159,8 +355,22 @@ step vac_full_specified: VACUUM (SKIP_LOCKED, FULL) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_access_exclusive vac_full_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_full_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -169,3 +379,17 @@ step vac_full_all_parts: VACUUM (SKIP_LOCKED, FULL) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
diff --git a/src/test/isolation/specs/vacuum-skip-locked.spec b/src/test/isolation/specs/vacuum-skip-locked.spec
index 3fad6e1c92a..b0da75d4b6d 100644
--- a/src/test/isolation/specs/vacuum-skip-locked.spec
+++ b/src/test/isolation/specs/vacuum-skip-locked.spec
@@ -33,6 +33,15 @@ step commit
COMMIT;
}
+step check_stat
+{
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+}
+
session s2
step vac_specified { VACUUM (SKIP_LOCKED) part1, part2; }
step vac_all_parts { VACUUM (SKIP_LOCKED) parted; }
@@ -43,19 +52,19 @@ step vac_analyze_all_parts { VACUUM (ANALYZE, SKIP_LOCKED) parted; }
step vac_full_specified { VACUUM (SKIP_LOCKED, FULL) part1, part2; }
step vac_full_all_parts { VACUUM (SKIP_LOCKED, FULL) parted; }
-permutation lock_share vac_specified commit
-permutation lock_share vac_all_parts commit
-permutation lock_share analyze_specified commit
-permutation lock_share analyze_all_parts commit
-permutation lock_share vac_analyze_specified commit
-permutation lock_share vac_analyze_all_parts commit
-permutation lock_share vac_full_specified commit
-permutation lock_share vac_full_all_parts commit
-permutation lock_access_exclusive vac_specified commit
-permutation lock_access_exclusive vac_all_parts commit
-permutation lock_access_exclusive analyze_specified commit
-permutation lock_access_exclusive analyze_all_parts commit
-permutation lock_access_exclusive vac_analyze_specified commit
-permutation lock_access_exclusive vac_analyze_all_parts commit
-permutation lock_access_exclusive vac_full_specified commit
-permutation lock_access_exclusive vac_full_all_parts commit
+permutation lock_share vac_specified commit check_stat
+permutation lock_share vac_all_parts commit check_stat
+permutation lock_share analyze_specified commit check_stat
+permutation lock_share analyze_all_parts commit check_stat
+permutation lock_share vac_analyze_specified commit check_stat
+permutation lock_share vac_analyze_all_parts commit check_stat
+permutation lock_share vac_full_specified commit check_stat
+permutation lock_share vac_full_all_parts commit check_stat
+permutation lock_access_exclusive vac_specified commit check_stat
+permutation lock_access_exclusive vac_all_parts commit check_stat
+permutation lock_access_exclusive analyze_specified commit check_stat
+permutation lock_access_exclusive analyze_all_parts commit check_stat
+permutation lock_access_exclusive vac_analyze_specified commit check_stat
+permutation lock_access_exclusive vac_analyze_all_parts commit check_stat
+permutation lock_access_exclusive vac_full_specified commit check_stat
+permutation lock_access_exclusive vac_full_all_parts commit check_stat
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index a65a5bf0c4f..9b2075d3373 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1835,13 +1835,21 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
+ pg_stat_get_last_skipped_vacuum_time(c.oid) AS last_skipped_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
+ pg_stat_get_last_skipped_analyze_time(c.oid) AS last_skipped_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
+ pg_stat_get_skipped_vacuum_count(c.oid) AS skipped_vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
+ pg_stat_get_skipped_analyze_count(c.oid) AS skipped_analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2346,13 +2354,21 @@ pg_stat_sys_tables| SELECT relid,
n_mod_since_analyze,
n_ins_since_vacuum,
last_vacuum,
+ last_skipped_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
+ last_skipped_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
+ skipped_vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
+ skipped_analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2401,13 +2417,21 @@ pg_stat_user_tables| SELECT relid,
n_mod_since_analyze,
n_ins_since_vacuum,
last_vacuum,
+ last_skipped_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
+ last_skipped_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
+ skipped_vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
+ skipped_analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
@ 2026-05-15 05:03 ` Yugo Nagata <nagata@sraoss.co.jp>
2026-05-15 09:02 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
0 siblings, 1 reply; 376+ messages in thread
From: Yugo Nagata @ 2026-05-15 05:03 UTC (permalink / raw)
To: Sami Imseih <samimseih@gmail.com>; +Cc: Zsolt Parragi <zsolt.parragi@percona.com>; Michael Paquier <michael@paquier.xyz>; pgsql-hackers
On Thu, 14 May 2026 19:59:18 -0500
Sami Imseih <samimseih@gmail.com> wrote:
> > Doesn't these also require a PGSTAT_FILE_FORMAT_ID change?
>
> right. that was missed. Fixed in the attached.
Thank you for updating the patch.
>
> > There's also an asymmetric case for the skipped counters, is that intentional?
> >
> > | Command | `skipped_vacuum_count` |
> > `skipped_analyze_count` |
> > |-----------------------------------------|------------------------|-------------------------|
> > | `VACUUM (FULL, ANALYZE, SKIP_LOCKED) t` | 0 | 1
> > |
> > | `VACUUM (ANALYZE, SKIP_LOCKED) t` | 1 | 1
> > |
> > | `VACUUM (FULL, SKIP_LOCKED) t` | 0 | 0
>
> Yeah, this is because vacuum_count and last_vacuum also skip VACUUM FULL.
> That was mentioned earlier in the thread.
Right.
> > > Initially, I was concerned that something might go wrong if a concurrent
> > > session performed DROP TABLE or ALTER TABLE RENAME between RangeVarGetRelidExtended()
> > > and RangeVarGetRelid(), but I could not find any actual issue. Even when the table
> > > name is changed, the correct statistics entry is updated correctly.
> >
> > A DROP TABLE can cause a missed skip in statistics, which is
> > reproducible with a custom injection point and tap test, see the
> > attached patch. The race window is quite minimal, but it exists.
>
> If the table is dropped, there are no stats to update. right?
In my analysis, even if the table is dropped or renamed just before calling RangeVarGetRelid()
(at the injection point you added), RangeVarGetRelid() still returns the table's OID. So it
seems possible that the statistics entry is updated locally, but it would be released shortly
afterward and thus eventually becomes invisible.
However, I'm not entirely sure whether this behavior is always guaranteed.
Could anyone clarify this?
Regards,
Yugo Nagata
--
Yugo Nagata <nagata@sraoss.co.jp>
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 05:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
@ 2026-05-15 09:02 ` Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 16:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
0 siblings, 1 reply; 376+ messages in thread
From: Zsolt Parragi @ 2026-05-15 09:02 UTC (permalink / raw)
To: pgsql-hackers@lists.postgresql.org
> If the table is dropped, there are no stats to update. right?
Ops, right. I focused too much on "all warnings should be visible in
the statistic, so the sum of warnings and statistics should match",
but of course that's not the case.
> However, I'm not entirely sure whether this behavior is always guaranteed.
> Could anyone clarify this?
There's another different corner-case if I move the injection point
inside pgstat_report_skipped_vacuum_analyze, after releasing the
syscache.
If the drop happens at that point, we insert an orphaned record into
the statistics, and it will be visible with the internal functions
(e.g. pg_stat_get_skipped_autoanalyze_count).
It is still invisible in the pg_stat_all_tables view, but now that
I've looked more at the code, I think internally it will stay there
permanently, even surviving pg_stat_reset?
> RangeVarGetRelid() still returns the table's OID
Yes, I also reached the same conclusion, I started testing because I
tried to see if I could break the double relid retrieval by some
scenarios (alter rename + create, drop-create etc), but it's not
possible. The ereports can execute CHECK_FOR_INTERRUPTS, but that
never calls ProcessCatchupInterrupt, and because of that it never runs
AccceptInvalidationMessages. And that's when I noticed that the
warning isn't visible in the statistics at all, and got distracted...
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 05:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-15 09:02 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
@ 2026-05-15 16:12 ` Sami Imseih <samimseih@gmail.com>
2026-05-15 20:21 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
0 siblings, 1 reply; 376+ messages in thread
From: Sami Imseih @ 2026-05-15 16:12 UTC (permalink / raw)
To: Zsolt Parragi <zsolt.parragi@percona.com>; +Cc: pgsql-hackers@lists.postgresql.org
> > If the table is dropped, there are no stats to update. right?
>
> Ops, right. I focused too much on "all warnings should be visible in
> the statistic, so the sum of warnings and statistics should match",
> but of course that's not the case.
>
> > However, I'm not entirely sure whether this behavior is always guaranteed.
> > Could anyone clarify this?
>
> There's another different corner-case if I move the injection point
> inside pgstat_report_skipped_vacuum_analyze, after releasing the
> syscache.
>
> If the drop happens at that point, we insert an orphaned record into
> the statistics, and it will be visible with the internal functions
> (e.g. pg_stat_get_skipped_autoanalyze_count).
>
> It is still invisible in the pg_stat_all_tables view, but now that
> I've looked more at the code, I think internally it will stay there
> permanently, even surviving pg_stat_reset?
Yeah, you’re right. I just realized that pgstat_get_entry_ref_locked()
creates an entry if one does not already exist, which means we can
leak entries if the table is dropped concurrently. After releasing the
syscache entry, we still have the relid, but the table may be dropped
before the stats update runs. In that case, the stats update
recreates the relation entry, and that entry is then leaked
permanently.
The tricky part of this feature is that we cannot hold a lock while updating
the entry, because the whole point of the stat is to track cases where
we failed to acquire a lock.
So, it seems pgstat_init_function_usage() deals with the same pattern
and they deal with it by doing a second syscache lookup after creating
the entry, and if the relation is gone, drop the entry. We can do the same
thing here. See v8-0001.
I also attached a v8-0002 building on Zsolt's injection point test and his
earlier suggestion to "move the injection point inside
pgstat_report_skipped_vacuum_analyze, after releasing the syscache".
These are isolation tests that rely on injection points. The tests are:
1. Table Drop + rollback. Lock is skipped and table is dropped but rolled
back. The stats are updated.
2. Table Drop + commit. Lock is skipped and table is dropped and committed.
No table entry to record a stat for.
The tests use pg_stat_get_skipped_vacuum_count to verify that no orphaned
entry is left behind.
--
Sami
Attachments:
[application/octet-stream] v8-0002-Add-injection-point-test-for-vacuum-skip_locked-s.patch (7.2K, ../../CAA5RZ0sVOTPCCbFfADO1+MTn60hmijP6QTDr0V=etQU=S5v=+g@mail.gmail.com/2-v8-0002-Add-injection-point-test-for-vacuum-skip_locked-s.patch)
download | inline diff:
From 6bdc0246507d0e5688ae8592bd59f69eb61811b3 Mon Sep 17 00:00:00 2001
From: Sami Imseih <samimseih@gmail.com>
Date: Fri, 15 May 2026 09:07:40 -0500
Subject: [PATCH v8 2/2] Add injection point test for vacuum skip_locked stats
race
Add an isolation test exercising the race window between VACUUM
(SKIP_LOCKED) reporting a skipped vacuum and concurrent table drops.
Two scenarios are tested:
1. Table dropped (committed) while vacuumer is blocked at the injection
point: no orphaned stats entry is created.
2. DROP TABLE rolled back while vacuumer is blocked: skip is still
recorded since the table and its stats entry survive.
---
src/backend/utils/activity/pgstat_relation.c | 2 +
.../expected/vacuum_skip_lock_stats.out | 91 +++++++++++++++++++
src/test/modules/injection_points/meson.build | 1 +
.../specs/vacuum_skip_lock_stats.spec | 67 ++++++++++++++
4 files changed, 161 insertions(+)
create mode 100644 src/test/modules/injection_points/expected/vacuum_skip_lock_stats.out
create mode 100644 src/test/modules/injection_points/specs/vacuum_skip_lock_stats.spec
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index f47c2958c1f..be6a6091460 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -21,6 +21,7 @@
#include "access/twophase_rmgr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
+#include "utils/injection_point.h"
#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/pgstat_internal.h"
@@ -391,6 +392,7 @@ pgstat_report_skipped_vacuum_analyze(Oid relid, int flags)
return; /* somebody deleted the rel, forget it */
isshared = ((Form_pg_class) GETSTRUCT(classTup))->relisshared;
ReleaseSysCache(classTup);
+ INJECTION_POINT("expand-vacuum-rel-skip-locked", NULL);
/* Store the data in the table's hash table entry. */
ts = GetCurrentTimestamp();
diff --git a/src/test/modules/injection_points/expected/vacuum_skip_lock_stats.out b/src/test/modules/injection_points/expected/vacuum_skip_lock_stats.out
new file mode 100644
index 00000000000..84c2180cc80
--- /dev/null
+++ b/src/test/modules/injection_points/expected/vacuum_skip_lock_stats.out
@@ -0,0 +1,91 @@
+Parsed test spec with 3 sessions
+
+starting permutation: lock vacuum unlock drop_table wakeup check_stats detach
+injection_points_attach
+-----------------------
+
+(1 row)
+
+step lock:
+ BEGIN;
+ LOCK TABLE test_skip IN ACCESS EXCLUSIVE MODE;
+
+s2: WARNING: skipping vacuum of "test_skip" --- lock not available
+step vacuum: VACUUM (SKIP_LOCKED) test_skip; <waiting ...>
+step unlock: COMMIT;
+step drop_table: DROP TABLE test_skip;
+step wakeup: SELECT injection_points_wakeup('expand-vacuum-rel-skip-locked');
+injection_points_wakeup
+-----------------------
+
+(1 row)
+
+step vacuum: <... completed>
+step check_stats:
+ SELECT pg_stat_force_next_flush();
+ SELECT pg_stat_get_skipped_vacuum_count(oid_val) AS skip_count
+ FROM saved_oid;
+
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+skip_count
+----------
+ 0
+(1 row)
+
+step detach: SELECT injection_points_detach('expand-vacuum-rel-skip-locked');
+injection_points_detach
+-----------------------
+
+(1 row)
+
+
+starting permutation: lock vacuum unlock rollback_drop wakeup check_stats detach
+injection_points_attach
+-----------------------
+
+(1 row)
+
+step lock:
+ BEGIN;
+ LOCK TABLE test_skip IN ACCESS EXCLUSIVE MODE;
+
+s2: WARNING: skipping vacuum of "test_skip" --- lock not available
+step vacuum: VACUUM (SKIP_LOCKED) test_skip; <waiting ...>
+step unlock: COMMIT;
+step rollback_drop:
+ BEGIN;
+ DROP TABLE test_skip;
+ ROLLBACK;
+
+step wakeup: SELECT injection_points_wakeup('expand-vacuum-rel-skip-locked');
+injection_points_wakeup
+-----------------------
+
+(1 row)
+
+step vacuum: <... completed>
+step check_stats:
+ SELECT pg_stat_force_next_flush();
+ SELECT pg_stat_get_skipped_vacuum_count(oid_val) AS skip_count
+ FROM saved_oid;
+
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+skip_count
+----------
+ 1
+(1 row)
+
+step detach: SELECT injection_points_detach('expand-vacuum-rel-skip-locked');
+injection_points_detach
+-----------------------
+
+(1 row)
+
diff --git a/src/test/modules/injection_points/meson.build b/src/test/modules/injection_points/meson.build
index 59dba1cb023..48cacfb81a8 100644
--- a/src/test/modules/injection_points/meson.build
+++ b/src/test/modules/injection_points/meson.build
@@ -51,6 +51,7 @@ tests += {
'repack_toast',
'syscache-update-pruned',
'heap_lock_update',
+ 'vacuum_skip_lock_stats',
],
'runningcheck': false, # see syscache-update-pruned
# Some tests wait for all snapshots, so avoid parallel execution
diff --git a/src/test/modules/injection_points/specs/vacuum_skip_lock_stats.spec b/src/test/modules/injection_points/specs/vacuum_skip_lock_stats.spec
new file mode 100644
index 00000000000..c37294a3ba1
--- /dev/null
+++ b/src/test/modules/injection_points/specs/vacuum_skip_lock_stats.spec
@@ -0,0 +1,67 @@
+# Test for race conditions between VACUUM (SKIP_LOCKED) stats reporting
+# and concurrent DROP TABLE.
+#
+# When VACUUM (SKIP_LOCKED) cannot acquire a lock, it reports skipped
+# statistics via pgstat_report_skipped_vacuum_analyze(). An injection
+# point after the syscache lookup but before the stats update allows us
+# to verify that a concurrent DROP does not leave orphaned stats entries.
+
+setup
+{
+ CREATE EXTENSION injection_points;
+ CREATE TABLE test_skip (id int);
+ INSERT INTO test_skip VALUES (1);
+ ANALYZE test_skip;
+ SELECT pg_stat_force_next_flush();
+ CREATE TABLE saved_oid (oid_val oid);
+ INSERT INTO saved_oid SELECT oid FROM pg_class WHERE relname = 'test_skip';
+}
+
+teardown
+{
+ DROP TABLE IF EXISTS test_skip;
+ DROP TABLE IF EXISTS saved_oid;
+ DROP EXTENSION injection_points;
+}
+
+# s1: holds the lock so VACUUM skips the table
+session s1
+step lock
+{
+ BEGIN;
+ LOCK TABLE test_skip IN ACCESS EXCLUSIVE MODE;
+}
+step unlock { COMMIT; }
+
+# s2: runs VACUUM (SKIP_LOCKED), blocks at injection point after skip
+session s2
+setup
+{
+ SELECT injection_points_set_local();
+ SELECT injection_points_attach('expand-vacuum-rel-skip-locked', 'wait');
+}
+step vacuum { VACUUM (SKIP_LOCKED) test_skip; }
+step detach { SELECT injection_points_detach('expand-vacuum-rel-skip-locked'); }
+
+# s3: drops table or wakes up the vacuumer
+session s3
+step drop_table { DROP TABLE test_skip; }
+step rollback_drop
+{
+ BEGIN;
+ DROP TABLE test_skip;
+ ROLLBACK;
+}
+step wakeup { SELECT injection_points_wakeup('expand-vacuum-rel-skip-locked'); }
+step check_stats
+{
+ SELECT pg_stat_force_next_flush();
+ SELECT pg_stat_get_skipped_vacuum_count(oid_val) AS skip_count
+ FROM saved_oid;
+}
+
+# Table dropped while vacuumer is blocked: no orphaned stats entry.
+permutation lock vacuum(wakeup) unlock drop_table wakeup check_stats detach
+
+# DROP rolled back while vacuumer is blocked: skip is still recorded.
+permutation lock vacuum(wakeup) unlock rollback_drop wakeup check_stats detach
--
2.50.1 (Apple Git-155)
[application/octet-stream] v8-0001-Track-skipped-vacuum-and-analyze-activity-per-rel.patch (40.7K, ../../CAA5RZ0sVOTPCCbFfADO1+MTn60hmijP6QTDr0V=etQU=S5v=+g@mail.gmail.com/3-v8-0001-Track-skipped-vacuum-and-analyze-activity-per-rel.patch)
download | inline diff:
From fd771372255adb03592990c81a961b9750165a40 Mon Sep 17 00:00:00 2001
From: Yugo Nagata <nagata@sraoss.co.jp>
Date: Tue, 24 Mar 2026 13:09:00 +0900
Subject: [PATCH v8 1/2] Track skipped vacuum and analyze activity per relation
This commit adds eight fields to the relation statistics that track
the last time vacuum or analyze has been attempted but skipped due to
lock unavailability, along with their counts:
- last_skipped_vacuum
- last_skipped_autovacuum
- last_skipped_analyze
- last_skipped_autoanalyze
- skipped_vacuum_count
- skipped_autovacuum_count
- skipped_analyze_count
- skipped_autoanalyze_count
These field can help users confirm that autovacuum is actively attempting
to run on a table that has not been vacuumed or analyzed for a long time,
and that the lack of progress is due to repeated skips rather than inactivity.
---
doc/src/sgml/monitoring.sgml | 88 ++++++
src/backend/catalog/system_views.sql | 8 +
src/backend/commands/vacuum.c | 31 +++
src/backend/utils/activity/pgstat_relation.c | 87 ++++++
src/backend/utils/adt/pgstatfuncs.c | 24 ++
src/include/catalog/pg_proc.dat | 32 +++
src/include/pgstat.h | 19 +-
.../isolation/expected/vacuum-skip-locked.out | 260 ++++++++++++++++--
.../isolation/specs/vacuum-skip-locked.spec | 41 +--
src/test/regress/expected/rules.out | 24 ++
10 files changed, 579 insertions(+), 35 deletions(-)
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 08d5b824552..a9b579d87a9 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4387,6 +4387,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_skipped_vacuum</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a manual vacuum on this table was attempted but skipped due to
+ lock unavailability (not counting <command>VACUUM FULL</command>)
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_autovacuum</structfield> <type>timestamp with time zone</type>
@@ -4397,6 +4407,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_skipped_autovacuum</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a vacuum on this table by the autovacuum daemon was attempted
+ but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_analyze</structfield> <type>timestamp with time zone</type>
@@ -4406,6 +4426,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_skipped_analyze</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a manual analyze on this table was attempted but skipped due to
+ lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_autoanalyze</structfield> <type>timestamp with time zone</type>
@@ -4416,6 +4446,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_skipped_autoanalyze</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time at which an analyze on this table by the autovacuum was
+ attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>vacuum_count</structfield> <type>bigint</type>
@@ -4426,6 +4466,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>skipped_vacuum_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times manual vacuums on this table have been attempted but skipped
+ due to lock unavailability (not counting <command>VACUUM FULL</command>)
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>autovacuum_count</structfield> <type>bigint</type>
@@ -4436,6 +4486,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>skipped_autovacuum_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times vacuums on this table by the autovacuum daemon have been
+ attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>analyze_count</structfield> <type>bigint</type>
@@ -4445,6 +4505,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>skipped_analyze_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times manual analyzes on this table have been attempted but
+ skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>autoanalyze_count</structfield> <type>bigint</type>
@@ -4455,6 +4525,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>skipped_autoanalyze_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times analyzes on this table by the autovacuum daemon have
+ been attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>total_vacuum_time</structfield> <type>double precision</type>
@@ -4510,6 +4590,14 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</tgroup>
</table>
+ <note>
+ <para>
+ When a manual vacuum or analyze on a parent table in an inheritance or
+ partitioning hierarchy is skipped, the statistics are recorded only for
+ the parent table, not for its children.
+ </para>
+ </note>
+
</sect2>
<sect2 id="monitoring-pg-stat-autovacuum-scores-view">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 73a1c1c4670..f509fc7876b 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -736,13 +736,21 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze,
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
+ pg_stat_get_last_skipped_vacuum_time(C.oid) as last_skipped_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
+ pg_stat_get_last_skipped_analyze_time(C.oid) as last_skipped_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
+ pg_stat_get_skipped_vacuum_count(C.oid) AS skipped_vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
+ pg_stat_get_skipped_analyze_count(C.oid) AS skipped_analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index a4abb29cf64..019cfa03679 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -793,8 +793,26 @@ vacuum_open_relation(Oid relid, RangeVar *relation, uint32 options,
rel = try_relation_open(relid, NoLock);
else
{
+ int flags = 0;
+
rel = NULL;
rel_lock = false;
+
+ if ((options & VACOPT_VACUUM) != 0 && (options & VACOPT_FULL) == 0)
+ {
+ if (AmAutoVacuumWorkerProcess())
+ flags |= PGSTAT_REPORT_SKIPPED_AUTOVACUUM;
+ else
+ flags |= PGSTAT_REPORT_SKIPPED_VACUUM;
+ }
+ if ((options & VACOPT_ANALYZE) != 0)
+ {
+ if (AmAutoVacuumWorkerProcess())
+ flags |= PGSTAT_REPORT_SKIPPED_AUTOANALYZE;
+ else
+ flags |= PGSTAT_REPORT_SKIPPED_ANALYZE;
+ }
+ pgstat_report_skipped_vacuum_analyze(relid, flags);
}
/* if relation is opened, leave */
@@ -930,6 +948,8 @@ expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context,
*/
if (!OidIsValid(relid))
{
+ int flags = 0;
+
if (options & VACOPT_VACUUM)
ereport(WARNING,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
@@ -940,6 +960,17 @@ expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
vrel->relation->relname)));
+
+ /* Get relid for statistics reporting */
+ relid = RangeVarGetRelid(vrel->relation, NoLock, true);
+
+ if ((options & VACOPT_VACUUM) != 0 && (options & VACOPT_FULL) == 0)
+ flags |= PGSTAT_REPORT_SKIPPED_VACUUM;
+ if ((options & VACOPT_ANALYZE) != 0)
+ flags |= PGSTAT_REPORT_SKIPPED_ANALYZE;
+
+ pgstat_report_skipped_vacuum_analyze(relid, flags);
+
return vacrels;
}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index b2ca28f83ba..f47c2958c1f 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -17,12 +17,15 @@
#include "postgres.h"
+#include "access/htup_details.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
+#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/pgstat_internal.h"
#include "utils/rel.h"
+#include "utils/syscache.h"
#include "utils/timestamp.h"
@@ -367,6 +370,90 @@ pgstat_report_analyze(Relation rel,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during vacuum or/and analyze.
+ */
+void
+pgstat_report_skipped_vacuum_analyze(Oid relid, int flags)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+ HeapTuple classTup;
+ bool isshared;
+
+ if (!pgstat_track_counts || !flags)
+ return;
+
+ classTup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(classTup))
+ return; /* somebody deleted the rel, forget it */
+ isshared = ((Form_pg_class) GETSTRUCT(classTup))->relisshared;
+ ReleaseSysCache(classTup);
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
+ isshared ? InvalidOid : MyDatabaseId,
+ relid, false);
+
+ /*
+ * Re-check whether the relation still exists. Unlike the normal stats
+ * reporting path, we don't hold a relation lock here (the whole point of
+ * SKIP_LOCKED is that we failed to acquire one). Without a lock, a
+ * concurrent DROP TABLE could commit between our earlier syscache lookup
+ * and the stats entry creation above, leaving an orphaned stats entry.
+ * This mirrors the approach used in pgstat_init_function_usage() for
+ * functions.
+ *
+ * Accept cache invalidation messages first, so that a DROP that committed
+ * after our earlier check is visible to the syscache lookup below.
+ */
+ AcceptInvalidationMessages();
+ classTup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(classTup))
+ {
+ pgstat_unlock_entry(entry_ref);
+ pgstat_drop_entry(PGSTAT_KIND_RELATION,
+ isshared ? InvalidOid : MyDatabaseId, relid);
+ return;
+ }
+ ReleaseSysCache(classTup);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+ if (flags & PGSTAT_REPORT_SKIPPED_VACUUM)
+ {
+ tabentry->last_skipped_vacuum_time = ts;
+ tabentry->skipped_vacuum_count++;
+ }
+ else if (flags & PGSTAT_REPORT_SKIPPED_AUTOVACUUM)
+ {
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+ }
+
+ if (flags & PGSTAT_REPORT_SKIPPED_ANALYZE)
+ {
+ tabentry->last_skipped_analyze_time = ts;
+ tabentry->skipped_analyze_count++;
+ }
+ else if (flags & PGSTAT_REPORT_SKIPPED_AUTOANALYZE)
+ {
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+ }
+
+ pgstat_unlock_entry(entry_ref);
+
+ /* see pgstat_report_vacuum() */
+ pgstat_flush_io(false);
+ (void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
+}
+
/*
* count a tuple insertion of n tuples
*/
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 6f9c9c72de5..c45126f18c3 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,18 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_analyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_analyze_count)
+
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
+/* pg_stat_get_skipped_vacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_vacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +182,18 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_analyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_analyze_time)
+
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
+/* pg_stat_get_last_skipped_vacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_vacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index be157a5fbe9..d72921a1626 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5681,6 +5681,38 @@
proargmodes => '{o,o,o,o,o,o,o,o,o,o}',
proargnames => '{oid,score,xid_score,mxid_score,vacuum_score,vacuum_insert_score,analyze_score,do_vacuum,do_analyze,for_wraparound}',
prosrc => 'pg_stat_get_autovacuum_scores' },
+{ oid => '8142', descr => 'statistics: last skipped vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_vacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_vacuum_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: last skipped analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_analyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_analyze_time' },
+{ oid => '8145', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8146', descr => 'statistics: number of skipped vacuum for a table',
+ proname => 'pg_stat_get_skipped_vacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_vacuum_count' },
+{ oid => '8147', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
+{ oid => '8148', descr => 'statistics: number of skipped analyzes for a table',
+ proname => 'pg_stat_get_skipped_analyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_analyze_count' },
+{ oid => '8149', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index dfa2e837638..e31871ed0cc 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -218,7 +218,7 @@ typedef struct PgStat_TableXactStatus
* ------------------------------------------------------------
*/
-#define PGSTAT_FILE_FORMAT_ID 0x01A5BCBC
+#define PGSTAT_FILE_FORMAT_ID 0x01A5BCBD
typedef struct PgStat_ArchiverStats
{
@@ -479,6 +479,15 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_vacuum_time; /* user initiated vacuum */
+ PgStat_Counter skipped_vacuum_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_analyze_time; /* user initiated */
+ PgStat_Counter skipped_analyze_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -703,6 +712,14 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+/* flags for pgstat_flush_backend() */
+#define PGSTAT_REPORT_SKIPPED_VACUUM (1 << 0) /* vacuum is skipped */
+#define PGSTAT_REPORT_SKIPPED_ANALYZE (1 << 1) /* analyze is skipped */
+#define PGSTAT_REPORT_SKIPPED_AUTOVACUUM (1 << 2) /* autovacuum is skipped */
+#define PGSTAT_REPORT_SKIPPED_AUTOANALYZE (1 << 3) /* autoanalyze is
+ * skipped */
+extern void pgstat_report_skipped_vacuum_analyze(Oid relid, int flags);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/isolation/expected/vacuum-skip-locked.out b/src/test/isolation/expected/vacuum-skip-locked.out
index 99db281a159..e2cff175b7c 100644
--- a/src/test/isolation/expected/vacuum-skip-locked.out
+++ b/src/test/isolation/expected/vacuum-skip-locked.out
@@ -1,6 +1,6 @@
Parsed test spec with 2 sessions
-starting permutation: lock_share vac_specified commit
+starting permutation: lock_share vac_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -10,8 +10,22 @@ step vac_specified: VACUUM (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -20,8 +34,22 @@ step vac_all_parts: VACUUM (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
+
-starting permutation: lock_share analyze_specified commit
+starting permutation: lock_share analyze_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -31,8 +59,22 @@ step analyze_specified: ANALYZE (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share analyze_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_share analyze_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -41,8 +83,22 @@ step analyze_all_parts: ANALYZE (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 1| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_share vac_analyze_specified commit
+starting permutation: lock_share vac_analyze_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -52,8 +108,22 @@ step vac_analyze_specified: VACUUM (ANALYZE, SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_analyze_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_analyze_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -62,8 +132,22 @@ step vac_analyze_all_parts: VACUUM (ANALYZE, SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 1| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_share vac_full_specified commit
+starting permutation: lock_share vac_full_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -73,8 +157,22 @@ step vac_full_specified: VACUUM (SKIP_LOCKED, FULL) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_full_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_full_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -83,8 +181,22 @@ step vac_full_all_parts: VACUUM (SKIP_LOCKED, FULL) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_specified commit
+
+starting permutation: lock_access_exclusive vac_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -94,8 +206,22 @@ step vac_specified: VACUUM (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_all_parts commit
+
+starting permutation: lock_access_exclusive vac_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -104,8 +230,22 @@ step vac_all_parts: VACUUM (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive analyze_specified commit
+
+starting permutation: lock_access_exclusive analyze_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -115,8 +255,22 @@ step analyze_specified: ANALYZE (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_access_exclusive analyze_all_parts commit
+starting permutation: lock_access_exclusive analyze_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -126,8 +280,22 @@ step commit:
COMMIT;
step analyze_all_parts: <... completed>
-
-starting permutation: lock_access_exclusive vac_analyze_specified commit
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 1| 0
+part1 | 0| 0| 1| 0
+part2 | 0| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_analyze_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -137,8 +305,22 @@ step vac_analyze_specified: VACUUM (ANALYZE, SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_analyze_all_parts commit
+
+starting permutation: lock_access_exclusive vac_analyze_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -148,8 +330,22 @@ step commit:
COMMIT;
step vac_analyze_all_parts: <... completed>
-
-starting permutation: lock_access_exclusive vac_full_specified commit
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 1| 0
+part1 | 1| 0| 1| 0
+part2 | 1| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_full_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -159,8 +355,22 @@ step vac_full_specified: VACUUM (SKIP_LOCKED, FULL) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_access_exclusive vac_full_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_full_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -169,3 +379,17 @@ step vac_full_all_parts: VACUUM (SKIP_LOCKED, FULL) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
diff --git a/src/test/isolation/specs/vacuum-skip-locked.spec b/src/test/isolation/specs/vacuum-skip-locked.spec
index 3fad6e1c92a..b0da75d4b6d 100644
--- a/src/test/isolation/specs/vacuum-skip-locked.spec
+++ b/src/test/isolation/specs/vacuum-skip-locked.spec
@@ -33,6 +33,15 @@ step commit
COMMIT;
}
+step check_stat
+{
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+}
+
session s2
step vac_specified { VACUUM (SKIP_LOCKED) part1, part2; }
step vac_all_parts { VACUUM (SKIP_LOCKED) parted; }
@@ -43,19 +52,19 @@ step vac_analyze_all_parts { VACUUM (ANALYZE, SKIP_LOCKED) parted; }
step vac_full_specified { VACUUM (SKIP_LOCKED, FULL) part1, part2; }
step vac_full_all_parts { VACUUM (SKIP_LOCKED, FULL) parted; }
-permutation lock_share vac_specified commit
-permutation lock_share vac_all_parts commit
-permutation lock_share analyze_specified commit
-permutation lock_share analyze_all_parts commit
-permutation lock_share vac_analyze_specified commit
-permutation lock_share vac_analyze_all_parts commit
-permutation lock_share vac_full_specified commit
-permutation lock_share vac_full_all_parts commit
-permutation lock_access_exclusive vac_specified commit
-permutation lock_access_exclusive vac_all_parts commit
-permutation lock_access_exclusive analyze_specified commit
-permutation lock_access_exclusive analyze_all_parts commit
-permutation lock_access_exclusive vac_analyze_specified commit
-permutation lock_access_exclusive vac_analyze_all_parts commit
-permutation lock_access_exclusive vac_full_specified commit
-permutation lock_access_exclusive vac_full_all_parts commit
+permutation lock_share vac_specified commit check_stat
+permutation lock_share vac_all_parts commit check_stat
+permutation lock_share analyze_specified commit check_stat
+permutation lock_share analyze_all_parts commit check_stat
+permutation lock_share vac_analyze_specified commit check_stat
+permutation lock_share vac_analyze_all_parts commit check_stat
+permutation lock_share vac_full_specified commit check_stat
+permutation lock_share vac_full_all_parts commit check_stat
+permutation lock_access_exclusive vac_specified commit check_stat
+permutation lock_access_exclusive vac_all_parts commit check_stat
+permutation lock_access_exclusive analyze_specified commit check_stat
+permutation lock_access_exclusive analyze_all_parts commit check_stat
+permutation lock_access_exclusive vac_analyze_specified commit check_stat
+permutation lock_access_exclusive vac_analyze_all_parts commit check_stat
+permutation lock_access_exclusive vac_full_specified commit check_stat
+permutation lock_access_exclusive vac_full_all_parts commit check_stat
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index a65a5bf0c4f..9b2075d3373 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1835,13 +1835,21 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
+ pg_stat_get_last_skipped_vacuum_time(c.oid) AS last_skipped_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
+ pg_stat_get_last_skipped_analyze_time(c.oid) AS last_skipped_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
+ pg_stat_get_skipped_vacuum_count(c.oid) AS skipped_vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
+ pg_stat_get_skipped_analyze_count(c.oid) AS skipped_analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2346,13 +2354,21 @@ pg_stat_sys_tables| SELECT relid,
n_mod_since_analyze,
n_ins_since_vacuum,
last_vacuum,
+ last_skipped_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
+ last_skipped_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
+ skipped_vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
+ skipped_analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2401,13 +2417,21 @@ pg_stat_user_tables| SELECT relid,
n_mod_since_analyze,
n_ins_since_vacuum,
last_vacuum,
+ last_skipped_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
+ last_skipped_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
+ skipped_vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
+ skipped_analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 05:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-15 09:02 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 16:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
@ 2026-05-15 20:21 ` Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-16 14:29 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
0 siblings, 1 reply; 376+ messages in thread
From: Zsolt Parragi @ 2026-05-15 20:21 UTC (permalink / raw)
To: pgsql-hackers@lists.postgresql.org
Looks good and seems to handle the previous corner case, I only have
two very minor comments:
@@ -391,6 +392,7 @@ pgstat_report_skipped_vacuum_analyze(Oid relid, int flags)
return; /* somebody deleted the rel, forget it */
isshared = ((Form_pg_class) GETSTRUCT(classTup))->relisshared;
ReleaseSysCache(classTup);
+ INJECTION_POINT("expand-vacuum-rel-skip-locked", NULL);
Nitpick, but now that the injection point lives inside
pgstat_report_skipped_vacuum_analyze, it should be named accordingly.
+ * This mirrors the approach used in pgstat_init_function_usage() for
+ * functions.
pgstat_init_function_usage only drops the entry conditionally if it
was created immediately before, so it is not entirely the same
pattern, I'm not sure if I would say "mirrors". Maybe similar to? But
this is again just nitpick.
The flow there (pending_entry, created flag => only rechecking on
freshly created) has the advantage that it only runs
AcceptInvalidationMessages+recheck when needed, but here it would also
require an additional pgstat_lock_entry call.
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 05:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-15 09:02 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 16:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 20:21 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
@ 2026-05-16 14:29 ` Sami Imseih <samimseih@gmail.com>
2026-07-11 08:23 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
0 siblings, 1 reply; 376+ messages in thread
From: Sami Imseih @ 2026-05-16 14:29 UTC (permalink / raw)
To: Zsolt Parragi <zsolt.parragi@percona.com>; +Cc: pgsql-hackers@lists.postgresql.org
> + INJECTION_POINT("expand-vacuum-rel-skip-locked", NULL);
> Nitpick, but now that the injection point lives inside
> pgstat_report_skipped_vacuum_analyze, it should be named accordingly.
right. I renamed it to skipped-vacuum-analyze-before-entry-lock
> Nitpick, but now that the injection point lives inside
> pgstat_report_skipped_vacuum_analyze, it should be named accordingly.
>
> + * This mirrors the approach used in pgstat_init_function_usage() for
> + * functions.
Fair. I just removed this comment. On re-reading, it really did not
add more value.
Attached v9.
Thanks!
--
Sami
Attachments:
[application/octet-stream] v9-0001-Track-skipped-vacuum-and-analyze-activity-per-rel.patch (40.6K, ../../CAA5RZ0spKxhnv0vCJu6nPe7mkOqOaXh83izkuvYHHzi4emY8kA@mail.gmail.com/2-v9-0001-Track-skipped-vacuum-and-analyze-activity-per-rel.patch)
download | inline diff:
From 7c3ce7c9c896b2712ccf8cdb000bd90a513fd7bb Mon Sep 17 00:00:00 2001
From: Yugo Nagata <nagata@sraoss.co.jp>
Date: Tue, 24 Mar 2026 13:09:00 +0900
Subject: [PATCH v9 1/2] Track skipped vacuum and analyze activity per relation
This commit adds eight fields to the relation statistics that track
the last time vacuum or analyze has been attempted but skipped due to
lock unavailability, along with their counts:
- last_skipped_vacuum
- last_skipped_autovacuum
- last_skipped_analyze
- last_skipped_autoanalyze
- skipped_vacuum_count
- skipped_autovacuum_count
- skipped_analyze_count
- skipped_autoanalyze_count
These field can help users confirm that autovacuum is actively attempting
to run on a table that has not been vacuumed or analyzed for a long time,
and that the lack of progress is due to repeated skips rather than inactivity.
---
doc/src/sgml/monitoring.sgml | 88 ++++++
src/backend/catalog/system_views.sql | 8 +
src/backend/commands/vacuum.c | 31 +++
src/backend/utils/activity/pgstat_relation.c | 85 ++++++
src/backend/utils/adt/pgstatfuncs.c | 24 ++
src/include/catalog/pg_proc.dat | 32 +++
src/include/pgstat.h | 19 +-
.../isolation/expected/vacuum-skip-locked.out | 260 ++++++++++++++++--
.../isolation/specs/vacuum-skip-locked.spec | 41 +--
src/test/regress/expected/rules.out | 24 ++
10 files changed, 577 insertions(+), 35 deletions(-)
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 08d5b824552..a9b579d87a9 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4387,6 +4387,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_skipped_vacuum</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a manual vacuum on this table was attempted but skipped due to
+ lock unavailability (not counting <command>VACUUM FULL</command>)
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_autovacuum</structfield> <type>timestamp with time zone</type>
@@ -4397,6 +4407,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_skipped_autovacuum</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a vacuum on this table by the autovacuum daemon was attempted
+ but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_analyze</structfield> <type>timestamp with time zone</type>
@@ -4406,6 +4426,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_skipped_analyze</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a manual analyze on this table was attempted but skipped due to
+ lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_autoanalyze</structfield> <type>timestamp with time zone</type>
@@ -4416,6 +4446,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_skipped_autoanalyze</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time at which an analyze on this table by the autovacuum was
+ attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>vacuum_count</structfield> <type>bigint</type>
@@ -4426,6 +4466,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>skipped_vacuum_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times manual vacuums on this table have been attempted but skipped
+ due to lock unavailability (not counting <command>VACUUM FULL</command>)
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>autovacuum_count</structfield> <type>bigint</type>
@@ -4436,6 +4486,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>skipped_autovacuum_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times vacuums on this table by the autovacuum daemon have been
+ attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>analyze_count</structfield> <type>bigint</type>
@@ -4445,6 +4505,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>skipped_analyze_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times manual analyzes on this table have been attempted but
+ skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>autoanalyze_count</structfield> <type>bigint</type>
@@ -4455,6 +4525,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>skipped_autoanalyze_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times analyzes on this table by the autovacuum daemon have
+ been attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>total_vacuum_time</structfield> <type>double precision</type>
@@ -4510,6 +4590,14 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</tgroup>
</table>
+ <note>
+ <para>
+ When a manual vacuum or analyze on a parent table in an inheritance or
+ partitioning hierarchy is skipped, the statistics are recorded only for
+ the parent table, not for its children.
+ </para>
+ </note>
+
</sect2>
<sect2 id="monitoring-pg-stat-autovacuum-scores-view">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 73a1c1c4670..f509fc7876b 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -736,13 +736,21 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze,
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
+ pg_stat_get_last_skipped_vacuum_time(C.oid) as last_skipped_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
+ pg_stat_get_last_skipped_analyze_time(C.oid) as last_skipped_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
+ pg_stat_get_skipped_vacuum_count(C.oid) AS skipped_vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
+ pg_stat_get_skipped_analyze_count(C.oid) AS skipped_analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index a4abb29cf64..019cfa03679 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -793,8 +793,26 @@ vacuum_open_relation(Oid relid, RangeVar *relation, uint32 options,
rel = try_relation_open(relid, NoLock);
else
{
+ int flags = 0;
+
rel = NULL;
rel_lock = false;
+
+ if ((options & VACOPT_VACUUM) != 0 && (options & VACOPT_FULL) == 0)
+ {
+ if (AmAutoVacuumWorkerProcess())
+ flags |= PGSTAT_REPORT_SKIPPED_AUTOVACUUM;
+ else
+ flags |= PGSTAT_REPORT_SKIPPED_VACUUM;
+ }
+ if ((options & VACOPT_ANALYZE) != 0)
+ {
+ if (AmAutoVacuumWorkerProcess())
+ flags |= PGSTAT_REPORT_SKIPPED_AUTOANALYZE;
+ else
+ flags |= PGSTAT_REPORT_SKIPPED_ANALYZE;
+ }
+ pgstat_report_skipped_vacuum_analyze(relid, flags);
}
/* if relation is opened, leave */
@@ -930,6 +948,8 @@ expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context,
*/
if (!OidIsValid(relid))
{
+ int flags = 0;
+
if (options & VACOPT_VACUUM)
ereport(WARNING,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
@@ -940,6 +960,17 @@ expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
vrel->relation->relname)));
+
+ /* Get relid for statistics reporting */
+ relid = RangeVarGetRelid(vrel->relation, NoLock, true);
+
+ if ((options & VACOPT_VACUUM) != 0 && (options & VACOPT_FULL) == 0)
+ flags |= PGSTAT_REPORT_SKIPPED_VACUUM;
+ if ((options & VACOPT_ANALYZE) != 0)
+ flags |= PGSTAT_REPORT_SKIPPED_ANALYZE;
+
+ pgstat_report_skipped_vacuum_analyze(relid, flags);
+
return vacrels;
}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index b2ca28f83ba..be9611b829b 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -17,12 +17,15 @@
#include "postgres.h"
+#include "access/htup_details.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
+#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/pgstat_internal.h"
#include "utils/rel.h"
+#include "utils/syscache.h"
#include "utils/timestamp.h"
@@ -367,6 +370,88 @@ pgstat_report_analyze(Relation rel,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during vacuum or/and analyze.
+ */
+void
+pgstat_report_skipped_vacuum_analyze(Oid relid, int flags)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+ HeapTuple classTup;
+ bool isshared;
+
+ if (!pgstat_track_counts || !flags)
+ return;
+
+ classTup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(classTup))
+ return; /* somebody deleted the rel, forget it */
+ isshared = ((Form_pg_class) GETSTRUCT(classTup))->relisshared;
+ ReleaseSysCache(classTup);
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
+ isshared ? InvalidOid : MyDatabaseId,
+ relid, false);
+
+ /*
+ * Re-check whether the relation still exists. Unlike the normal stats
+ * reporting path, we don't hold a relation lock here (the whole point of
+ * SKIP_LOCKED is that we failed to acquire one). Without a lock, a
+ * concurrent DROP TABLE could commit between our earlier syscache lookup
+ * and the stats entry creation above, leaving an orphaned stats entry.
+ *
+ * Accept cache invalidation messages first, so that a DROP that committed
+ * after our earlier check is visible to the syscache lookup below.
+ */
+ AcceptInvalidationMessages();
+ classTup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(classTup))
+ {
+ pgstat_unlock_entry(entry_ref);
+ pgstat_drop_entry(PGSTAT_KIND_RELATION,
+ isshared ? InvalidOid : MyDatabaseId, relid);
+ return;
+ }
+ ReleaseSysCache(classTup);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+ if (flags & PGSTAT_REPORT_SKIPPED_VACUUM)
+ {
+ tabentry->last_skipped_vacuum_time = ts;
+ tabentry->skipped_vacuum_count++;
+ }
+ else if (flags & PGSTAT_REPORT_SKIPPED_AUTOVACUUM)
+ {
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+ }
+
+ if (flags & PGSTAT_REPORT_SKIPPED_ANALYZE)
+ {
+ tabentry->last_skipped_analyze_time = ts;
+ tabentry->skipped_analyze_count++;
+ }
+ else if (flags & PGSTAT_REPORT_SKIPPED_AUTOANALYZE)
+ {
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+ }
+
+ pgstat_unlock_entry(entry_ref);
+
+ /* see pgstat_report_vacuum() */
+ pgstat_flush_io(false);
+ (void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
+}
+
/*
* count a tuple insertion of n tuples
*/
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 6f9c9c72de5..c45126f18c3 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,18 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_analyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_analyze_count)
+
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
+/* pg_stat_get_skipped_vacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_vacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +182,18 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_analyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_analyze_time)
+
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
+/* pg_stat_get_last_skipped_vacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_vacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index be157a5fbe9..d72921a1626 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5681,6 +5681,38 @@
proargmodes => '{o,o,o,o,o,o,o,o,o,o}',
proargnames => '{oid,score,xid_score,mxid_score,vacuum_score,vacuum_insert_score,analyze_score,do_vacuum,do_analyze,for_wraparound}',
prosrc => 'pg_stat_get_autovacuum_scores' },
+{ oid => '8142', descr => 'statistics: last skipped vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_vacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_vacuum_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: last skipped analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_analyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_analyze_time' },
+{ oid => '8145', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8146', descr => 'statistics: number of skipped vacuum for a table',
+ proname => 'pg_stat_get_skipped_vacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_vacuum_count' },
+{ oid => '8147', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
+{ oid => '8148', descr => 'statistics: number of skipped analyzes for a table',
+ proname => 'pg_stat_get_skipped_analyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_analyze_count' },
+{ oid => '8149', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index dfa2e837638..e31871ed0cc 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -218,7 +218,7 @@ typedef struct PgStat_TableXactStatus
* ------------------------------------------------------------
*/
-#define PGSTAT_FILE_FORMAT_ID 0x01A5BCBC
+#define PGSTAT_FILE_FORMAT_ID 0x01A5BCBD
typedef struct PgStat_ArchiverStats
{
@@ -479,6 +479,15 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_vacuum_time; /* user initiated vacuum */
+ PgStat_Counter skipped_vacuum_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_analyze_time; /* user initiated */
+ PgStat_Counter skipped_analyze_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -703,6 +712,14 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+/* flags for pgstat_flush_backend() */
+#define PGSTAT_REPORT_SKIPPED_VACUUM (1 << 0) /* vacuum is skipped */
+#define PGSTAT_REPORT_SKIPPED_ANALYZE (1 << 1) /* analyze is skipped */
+#define PGSTAT_REPORT_SKIPPED_AUTOVACUUM (1 << 2) /* autovacuum is skipped */
+#define PGSTAT_REPORT_SKIPPED_AUTOANALYZE (1 << 3) /* autoanalyze is
+ * skipped */
+extern void pgstat_report_skipped_vacuum_analyze(Oid relid, int flags);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/isolation/expected/vacuum-skip-locked.out b/src/test/isolation/expected/vacuum-skip-locked.out
index 99db281a159..e2cff175b7c 100644
--- a/src/test/isolation/expected/vacuum-skip-locked.out
+++ b/src/test/isolation/expected/vacuum-skip-locked.out
@@ -1,6 +1,6 @@
Parsed test spec with 2 sessions
-starting permutation: lock_share vac_specified commit
+starting permutation: lock_share vac_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -10,8 +10,22 @@ step vac_specified: VACUUM (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -20,8 +34,22 @@ step vac_all_parts: VACUUM (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
+
-starting permutation: lock_share analyze_specified commit
+starting permutation: lock_share analyze_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -31,8 +59,22 @@ step analyze_specified: ANALYZE (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share analyze_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_share analyze_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -41,8 +83,22 @@ step analyze_all_parts: ANALYZE (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 1| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_share vac_analyze_specified commit
+starting permutation: lock_share vac_analyze_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -52,8 +108,22 @@ step vac_analyze_specified: VACUUM (ANALYZE, SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_analyze_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_analyze_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -62,8 +132,22 @@ step vac_analyze_all_parts: VACUUM (ANALYZE, SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 1| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_share vac_full_specified commit
+starting permutation: lock_share vac_full_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -73,8 +157,22 @@ step vac_full_specified: VACUUM (SKIP_LOCKED, FULL) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_full_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_full_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -83,8 +181,22 @@ step vac_full_all_parts: VACUUM (SKIP_LOCKED, FULL) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_specified commit
+
+starting permutation: lock_access_exclusive vac_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -94,8 +206,22 @@ step vac_specified: VACUUM (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_all_parts commit
+
+starting permutation: lock_access_exclusive vac_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -104,8 +230,22 @@ step vac_all_parts: VACUUM (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive analyze_specified commit
+
+starting permutation: lock_access_exclusive analyze_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -115,8 +255,22 @@ step analyze_specified: ANALYZE (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_access_exclusive analyze_all_parts commit
+starting permutation: lock_access_exclusive analyze_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -126,8 +280,22 @@ step commit:
COMMIT;
step analyze_all_parts: <... completed>
-
-starting permutation: lock_access_exclusive vac_analyze_specified commit
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 1| 0
+part1 | 0| 0| 1| 0
+part2 | 0| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_analyze_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -137,8 +305,22 @@ step vac_analyze_specified: VACUUM (ANALYZE, SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_analyze_all_parts commit
+
+starting permutation: lock_access_exclusive vac_analyze_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -148,8 +330,22 @@ step commit:
COMMIT;
step vac_analyze_all_parts: <... completed>
-
-starting permutation: lock_access_exclusive vac_full_specified commit
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 1| 0
+part1 | 1| 0| 1| 0
+part2 | 1| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_full_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -159,8 +355,22 @@ step vac_full_specified: VACUUM (SKIP_LOCKED, FULL) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_access_exclusive vac_full_all_parts commit
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_full_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -169,3 +379,17 @@ step vac_full_all_parts: VACUUM (SKIP_LOCKED, FULL) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|skipped_vacuum_count|analyze_count|skipped_analyze_count
+-------+------------+--------------------+-------------+---------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
diff --git a/src/test/isolation/specs/vacuum-skip-locked.spec b/src/test/isolation/specs/vacuum-skip-locked.spec
index 3fad6e1c92a..b0da75d4b6d 100644
--- a/src/test/isolation/specs/vacuum-skip-locked.spec
+++ b/src/test/isolation/specs/vacuum-skip-locked.spec
@@ -33,6 +33,15 @@ step commit
COMMIT;
}
+step check_stat
+{
+ SELECT relname,
+ vacuum_count, skipped_vacuum_count,
+ analyze_count, skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+}
+
session s2
step vac_specified { VACUUM (SKIP_LOCKED) part1, part2; }
step vac_all_parts { VACUUM (SKIP_LOCKED) parted; }
@@ -43,19 +52,19 @@ step vac_analyze_all_parts { VACUUM (ANALYZE, SKIP_LOCKED) parted; }
step vac_full_specified { VACUUM (SKIP_LOCKED, FULL) part1, part2; }
step vac_full_all_parts { VACUUM (SKIP_LOCKED, FULL) parted; }
-permutation lock_share vac_specified commit
-permutation lock_share vac_all_parts commit
-permutation lock_share analyze_specified commit
-permutation lock_share analyze_all_parts commit
-permutation lock_share vac_analyze_specified commit
-permutation lock_share vac_analyze_all_parts commit
-permutation lock_share vac_full_specified commit
-permutation lock_share vac_full_all_parts commit
-permutation lock_access_exclusive vac_specified commit
-permutation lock_access_exclusive vac_all_parts commit
-permutation lock_access_exclusive analyze_specified commit
-permutation lock_access_exclusive analyze_all_parts commit
-permutation lock_access_exclusive vac_analyze_specified commit
-permutation lock_access_exclusive vac_analyze_all_parts commit
-permutation lock_access_exclusive vac_full_specified commit
-permutation lock_access_exclusive vac_full_all_parts commit
+permutation lock_share vac_specified commit check_stat
+permutation lock_share vac_all_parts commit check_stat
+permutation lock_share analyze_specified commit check_stat
+permutation lock_share analyze_all_parts commit check_stat
+permutation lock_share vac_analyze_specified commit check_stat
+permutation lock_share vac_analyze_all_parts commit check_stat
+permutation lock_share vac_full_specified commit check_stat
+permutation lock_share vac_full_all_parts commit check_stat
+permutation lock_access_exclusive vac_specified commit check_stat
+permutation lock_access_exclusive vac_all_parts commit check_stat
+permutation lock_access_exclusive analyze_specified commit check_stat
+permutation lock_access_exclusive analyze_all_parts commit check_stat
+permutation lock_access_exclusive vac_analyze_specified commit check_stat
+permutation lock_access_exclusive vac_analyze_all_parts commit check_stat
+permutation lock_access_exclusive vac_full_specified commit check_stat
+permutation lock_access_exclusive vac_full_all_parts commit check_stat
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index a65a5bf0c4f..9b2075d3373 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1835,13 +1835,21 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
+ pg_stat_get_last_skipped_vacuum_time(c.oid) AS last_skipped_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
+ pg_stat_get_last_skipped_analyze_time(c.oid) AS last_skipped_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
+ pg_stat_get_skipped_vacuum_count(c.oid) AS skipped_vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
+ pg_stat_get_skipped_analyze_count(c.oid) AS skipped_analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2346,13 +2354,21 @@ pg_stat_sys_tables| SELECT relid,
n_mod_since_analyze,
n_ins_since_vacuum,
last_vacuum,
+ last_skipped_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
+ last_skipped_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
+ skipped_vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
+ skipped_analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2401,13 +2417,21 @@ pg_stat_user_tables| SELECT relid,
n_mod_since_analyze,
n_ins_since_vacuum,
last_vacuum,
+ last_skipped_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
+ last_skipped_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
+ skipped_vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
+ skipped_analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.50.1 (Apple Git-155)
[application/octet-stream] v9-0002-Add-injection-point-test-for-vacuum-skip_locked-s.patch (7.3K, ../../CAA5RZ0spKxhnv0vCJu6nPe7mkOqOaXh83izkuvYHHzi4emY8kA@mail.gmail.com/3-v9-0002-Add-injection-point-test-for-vacuum-skip_locked-s.patch)
download | inline diff:
From b555f0e6d91f67fe59a300d1ec7f3f1d74fd760f Mon Sep 17 00:00:00 2001
From: Sami Imseih <samimseih@gmail.com>
Date: Fri, 15 May 2026 09:07:40 -0500
Subject: [PATCH v9 2/2] Add injection point test for vacuum skip_locked stats
Add an isolation test exercising the race window between VACUUM
(SKIP_LOCKED) reporting a skipped vacuum and concurrent table drops.
Two scenarios are tested:
1. Table dropped (committed) while vacuumer is blocked at the injection
point: no orphaned stats entry is created.
2. DROP TABLE rolled back while vacuumer is blocked: skip is still
recorded since the table and its stats entry survive.
---
src/backend/utils/activity/pgstat_relation.c | 2 +
.../expected/vacuum_skip_lock_stats.out | 91 +++++++++++++++++++
src/test/modules/injection_points/meson.build | 1 +
.../specs/vacuum_skip_lock_stats.spec | 67 ++++++++++++++
4 files changed, 161 insertions(+)
create mode 100644 src/test/modules/injection_points/expected/vacuum_skip_lock_stats.out
create mode 100644 src/test/modules/injection_points/specs/vacuum_skip_lock_stats.spec
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index be9611b829b..3f9faf4c78b 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -21,6 +21,7 @@
#include "access/twophase_rmgr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
+#include "utils/injection_point.h"
#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/pgstat_internal.h"
@@ -391,6 +392,7 @@ pgstat_report_skipped_vacuum_analyze(Oid relid, int flags)
return; /* somebody deleted the rel, forget it */
isshared = ((Form_pg_class) GETSTRUCT(classTup))->relisshared;
ReleaseSysCache(classTup);
+ INJECTION_POINT("skipped-vacuum-analyze-before-entry-lock", NULL);
/* Store the data in the table's hash table entry. */
ts = GetCurrentTimestamp();
diff --git a/src/test/modules/injection_points/expected/vacuum_skip_lock_stats.out b/src/test/modules/injection_points/expected/vacuum_skip_lock_stats.out
new file mode 100644
index 00000000000..0bb44b43632
--- /dev/null
+++ b/src/test/modules/injection_points/expected/vacuum_skip_lock_stats.out
@@ -0,0 +1,91 @@
+Parsed test spec with 3 sessions
+
+starting permutation: lock vacuum unlock drop_table wakeup check_stats detach
+injection_points_attach
+-----------------------
+
+(1 row)
+
+step lock:
+ BEGIN;
+ LOCK TABLE test_skip IN ACCESS EXCLUSIVE MODE;
+
+s2: WARNING: skipping vacuum of "test_skip" --- lock not available
+step vacuum: VACUUM (SKIP_LOCKED) test_skip; <waiting ...>
+step unlock: COMMIT;
+step drop_table: DROP TABLE test_skip;
+step wakeup: SELECT injection_points_wakeup('skipped-vacuum-analyze-before-entry-lock');
+injection_points_wakeup
+-----------------------
+
+(1 row)
+
+step vacuum: <... completed>
+step check_stats:
+ SELECT pg_stat_force_next_flush();
+ SELECT pg_stat_get_skipped_vacuum_count(oid_val) AS skip_count
+ FROM saved_oid;
+
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+skip_count
+----------
+ 0
+(1 row)
+
+step detach: SELECT injection_points_detach('skipped-vacuum-analyze-before-entry-lock');
+injection_points_detach
+-----------------------
+
+(1 row)
+
+
+starting permutation: lock vacuum unlock rollback_drop wakeup check_stats detach
+injection_points_attach
+-----------------------
+
+(1 row)
+
+step lock:
+ BEGIN;
+ LOCK TABLE test_skip IN ACCESS EXCLUSIVE MODE;
+
+s2: WARNING: skipping vacuum of "test_skip" --- lock not available
+step vacuum: VACUUM (SKIP_LOCKED) test_skip; <waiting ...>
+step unlock: COMMIT;
+step rollback_drop:
+ BEGIN;
+ DROP TABLE test_skip;
+ ROLLBACK;
+
+step wakeup: SELECT injection_points_wakeup('skipped-vacuum-analyze-before-entry-lock');
+injection_points_wakeup
+-----------------------
+
+(1 row)
+
+step vacuum: <... completed>
+step check_stats:
+ SELECT pg_stat_force_next_flush();
+ SELECT pg_stat_get_skipped_vacuum_count(oid_val) AS skip_count
+ FROM saved_oid;
+
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+skip_count
+----------
+ 1
+(1 row)
+
+step detach: SELECT injection_points_detach('skipped-vacuum-analyze-before-entry-lock');
+injection_points_detach
+-----------------------
+
+(1 row)
+
diff --git a/src/test/modules/injection_points/meson.build b/src/test/modules/injection_points/meson.build
index 59dba1cb023..48cacfb81a8 100644
--- a/src/test/modules/injection_points/meson.build
+++ b/src/test/modules/injection_points/meson.build
@@ -51,6 +51,7 @@ tests += {
'repack_toast',
'syscache-update-pruned',
'heap_lock_update',
+ 'vacuum_skip_lock_stats',
],
'runningcheck': false, # see syscache-update-pruned
# Some tests wait for all snapshots, so avoid parallel execution
diff --git a/src/test/modules/injection_points/specs/vacuum_skip_lock_stats.spec b/src/test/modules/injection_points/specs/vacuum_skip_lock_stats.spec
new file mode 100644
index 00000000000..de81e4c67c4
--- /dev/null
+++ b/src/test/modules/injection_points/specs/vacuum_skip_lock_stats.spec
@@ -0,0 +1,67 @@
+# Test for race conditions between VACUUM (SKIP_LOCKED) stats reporting
+# and concurrent DROP TABLE.
+#
+# When VACUUM (SKIP_LOCKED) cannot acquire a lock, it reports skipped
+# statistics via pgstat_report_skipped_vacuum_analyze(). An injection
+# point after the syscache lookup but before the stats update allows us
+# to verify that a concurrent DROP does not leave orphaned stats entries.
+
+setup
+{
+ CREATE EXTENSION injection_points;
+ CREATE TABLE test_skip (id int);
+ INSERT INTO test_skip VALUES (1);
+ ANALYZE test_skip;
+ SELECT pg_stat_force_next_flush();
+ CREATE TABLE saved_oid (oid_val oid);
+ INSERT INTO saved_oid SELECT oid FROM pg_class WHERE relname = 'test_skip';
+}
+
+teardown
+{
+ DROP TABLE IF EXISTS test_skip;
+ DROP TABLE IF EXISTS saved_oid;
+ DROP EXTENSION injection_points;
+}
+
+# s1: holds the lock so VACUUM skips the table
+session s1
+step lock
+{
+ BEGIN;
+ LOCK TABLE test_skip IN ACCESS EXCLUSIVE MODE;
+}
+step unlock { COMMIT; }
+
+# s2: runs VACUUM (SKIP_LOCKED), blocks at injection point after skip
+session s2
+setup
+{
+ SELECT injection_points_set_local();
+ SELECT injection_points_attach('skipped-vacuum-analyze-before-entry-lock', 'wait');
+}
+step vacuum { VACUUM (SKIP_LOCKED) test_skip; }
+step detach { SELECT injection_points_detach('skipped-vacuum-analyze-before-entry-lock'); }
+
+# s3: drops table or wakes up the vacuumer
+session s3
+step drop_table { DROP TABLE test_skip; }
+step rollback_drop
+{
+ BEGIN;
+ DROP TABLE test_skip;
+ ROLLBACK;
+}
+step wakeup { SELECT injection_points_wakeup('skipped-vacuum-analyze-before-entry-lock'); }
+step check_stats
+{
+ SELECT pg_stat_force_next_flush();
+ SELECT pg_stat_get_skipped_vacuum_count(oid_val) AS skip_count
+ FROM saved_oid;
+}
+
+# Table dropped while vacuumer is blocked: no orphaned stats entry.
+permutation lock vacuum(wakeup) unlock drop_table wakeup check_stats detach
+
+# DROP rolled back while vacuumer is blocked: skip is still recorded.
+permutation lock vacuum(wakeup) unlock rollback_drop wakeup check_stats detach
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 05:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-15 09:02 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 16:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 20:21 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-16 14:29 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
@ 2026-07-11 08:23 ` Shinya Kato <shinya11.kato@gmail.com>
2026-07-21 18:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
0 siblings, 1 reply; 376+ messages in thread
From: Shinya Kato @ 2026-07-11 08:23 UTC (permalink / raw)
To: Sami Imseih <samimseih@gmail.com>; +Cc: Zsolt Parragi <zsolt.parragi@percona.com>; pgsql-hackers@lists.postgresql.org
Hi,
On Sat, May 16, 2026 at 11:29 PM Sami Imseih <samimseih@gmail.com> wrote:
> Attached v9.
Thanks for working on this. I read through v9 and I am +1 on the feature.
It does not build against master for me:
pgstat_relation.c: In function ‘pgstat_report_skipped_vacuum_analyze’:
pgstat_relation.c:419:17: error: too few arguments to function
‘pgstat_drop_entry’
419 | pgstat_drop_entry(PGSTAT_KIND_RELATION,
| ^~~~~~~~~~~~~~~~~
In file included from pgstat_relation.c:27:
../../../../src/include/utils/pgstat_internal.h:811:13: note: declared here
811 | extern bool pgstat_drop_entry(PgStat_Kind kind, Oid dboid, uint64 objid,
| ^~~~~~~~~~~~~~~~~
make[4]: *** [<builtin>: pgstat_relation.o] Error 1
pgstat_drop_entry() gained a missing_ok argument in commit 850b9218c8e
[1], so the call in pgstat_report_skipped_vacuum_analyze() needs a
fourth argument. Passing true, the way the function-usage path in
pgstat_function.c now does, builds cleanly.
On the field names: every skip these fields count is a lock skip, but
skipped_vacuum_count, last_skipped_vacuum and friends do not say so.
In the "Add autovacuum_warning to surface concurrent vacuum
collisions" thread [2], I raised another skip worth tracking, an
autovacuum worker skipping a table because another worker is already
vacuuming it, as an early sign that autovacuum is not keeping up.
There it was suggested to expose it as a per-table counter in
pg_stat_all_tables, the same surface this patch builds. That needs its
own discussion and I am not proposing to widen this patch for it.
But since another skip reason may be added later, I think the column
name should make clear the skip is due to lock unavailability. What do
you think?
[1] https://postgr.es/c/850b9218c8e
[2] https://www.postgresql.org/message-id/CALj2ACUYYv_z3C5VkcYmyezbqxUuJ%2B0H1GeNGZqPK3WFexmTYw%40mail.g...
--
Best regards,
Shinya Kato
NTT OSS Center
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 05:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-15 09:02 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 16:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 20:21 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-16 14:29 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-11 08:23 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
@ 2026-07-21 18:12 ` Sami Imseih <samimseih@gmail.com>
2026-07-21 18:51 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
0 siblings, 1 reply; 376+ messages in thread
From: Sami Imseih @ 2026-07-21 18:12 UTC (permalink / raw)
To: Shinya Kato <shinya11.kato@gmail.com>; +Cc: Zsolt Parragi <zsolt.parragi@percona.com>; pgsql-hackers@lists.postgresql.org
> Thanks for working on this. I read through v9 and I am +1 on the feature.
>
> It does not build against master for me:
>
> pgstat_relation.c: In function ‘pgstat_report_skipped_vacuum_analyze’:
> pgstat_relation.c:419:17: error: too few arguments to function
> ‘pgstat_drop_entry’
> 419 | pgstat_drop_entry(PGSTAT_KIND_RELATION,
> | ^~~~~~~~~~~~~~~~~
> In file included from pgstat_relation.c:27:
> ../../../../src/include/utils/pgstat_internal.h:811:13: note: declared here
> 811 | extern bool pgstat_drop_entry(PgStat_Kind kind, Oid dboid, uint64 objid,
> | ^~~~~~~~~~~~~~~~~
> make[4]: *** [<builtin>: pgstat_relation.o] Error 1
>
> pgstat_drop_entry() gained a missing_ok argument in commit 850b9218c8e
> [1], so the call in pgstat_report_skipped_vacuum_analyze() needs a
> fourth argument. Passing true, the way the function-usage path in
> pgstat_function.c now does, builds cleanly.
Correct. This is due to 850b9218c8e4. I added 'true' to silently ignore
concurrently dropped tables.
> On the field names: every skip these fields count is a lock skip, but
> skipped_vacuum_count, last_skipped_vacuum and friends do not say so.
>
> In the "Add autovacuum_warning to surface concurrent vacuum
> collisions" thread [2], I raised another skip worth tracking, an
> autovacuum worker skipping a table because another worker is already
> vacuuming it, as an early sign that autovacuum is not keeping up.
> There it was suggested to expose it as a per-table counter in
> pg_stat_all_tables, the same surface this patch builds. That needs its
> own discussion and I am not proposing to widen this patch for it.
>
> But since another skip reason may be added later, I think the column
> name should make clear the skip is due to lock unavailability. What do
> you think?
>
> [1] https://postgr.es/c/850b9218c8e
> [2] https://www.postgresql.org/message-id/CALj2ACUYYv_z3C5VkcYmyezbqxUuJ%2B0H1GeNGZqPK3WFexmTYw%40mail.g...
I am not sure about this. I read [1], and I really don't think that
showing the message " table ... is already being vacuumed by another worker"
or tracking a counter for this case is needed, but we should keep that
discussion
in that thread.
However, I still agree with your point about making the column name more
precise, so doing something like *_lock_skipped_* is better. I don't
foresee this,
but if we do ever track another skip reason besides locking, it will need to
be a separate counter.
See v10.
[1] https://www.postgresql.org/message-id/flat/CALj2ACUYYv_z3C5VkcYmyezbqxUuJ%2B0H1GeNGZqPK3WFexmTYw%40m...
--
Sami Imseih
Amazon Web Services (AWS)
Attachments:
[application/octet-stream] v10-0001-Track-skipped-vacuum-and-analyze-activity-per-re.patch (42.3K, ../../CAA5RZ0txJaRyRnwWQiKEsOGYxU7FhKhX49qC5hZKPU7eSQz24Q@mail.gmail.com/2-v10-0001-Track-skipped-vacuum-and-analyze-activity-per-re.patch)
download | inline diff:
From 848d3e1065c2fb3aa1c64e80c18c4dad6c4369a5 Mon Sep 17 00:00:00 2001
From: Yugo Nagata <nagata@sraoss.co.jp>
Date: Tue, 24 Mar 2026 13:09:00 +0900
Subject: [PATCH v10 1/1] Track skipped vacuum and analyze activity per
relation
This commit adds eight fields to the relation statistics that track
the last time vacuum or analyze has been attempted but skipped due to
lock unavailability, along with their counts:
- last_lock_skipped_vacuum
- last_lock_skipped_autovacuum
- last_lock_skipped_analyze
- last_lock_skipped_autoanalyze
- lock_skipped_vacuum_count
- lock_skipped_autovacuum_count
- lock_skipped_analyze_count
- lock_skipped_autoanalyze_count
The field names make explicit that the skip is due to lock
unavailability, distinguishing it from other reasons a vacuum or
analyze might not run on a relation.
These fields can help users confirm that autovacuum is actively attempting
to run on a table that has not been vacuumed or analyzed for a long time,
and that the lack of progress is due to repeated skips rather than inactivity.
---
doc/src/sgml/monitoring.sgml | 88 ++++++
src/backend/catalog/system_views.sql | 8 +
src/backend/commands/vacuum.c | 31 +++
src/backend/utils/activity/pgstat_relation.c | 85 ++++++
src/backend/utils/adt/pgstatfuncs.c | 24 ++
src/include/catalog/pg_proc.dat | 32 +++
src/include/pgstat.h | 19 +-
.../isolation/expected/vacuum-skip-locked.out | 260 ++++++++++++++++--
.../isolation/specs/vacuum-skip-locked.spec | 41 +--
src/test/regress/expected/rules.out | 24 ++
10 files changed, 577 insertions(+), 35 deletions(-)
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index d1a20d001e9..2912ec975d6 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4528,6 +4528,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_lock_skipped_vacuum</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a manual vacuum on this table was attempted but skipped due to
+ lock unavailability (not counting <command>VACUUM FULL</command>)
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_autovacuum</structfield> <type>timestamp with time zone</type>
@@ -4538,6 +4548,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_lock_skipped_autovacuum</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a vacuum on this table by the autovacuum daemon was attempted
+ but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_analyze</structfield> <type>timestamp with time zone</type>
@@ -4547,6 +4567,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_lock_skipped_analyze</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a manual analyze on this table was attempted but skipped due to
+ lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_autoanalyze</structfield> <type>timestamp with time zone</type>
@@ -4557,6 +4587,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_lock_skipped_autoanalyze</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time at which an analyze on this table by the autovacuum was
+ attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>vacuum_count</structfield> <type>bigint</type>
@@ -4567,6 +4607,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>lock_skipped_vacuum_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times manual vacuums on this table have been attempted but skipped
+ due to lock unavailability (not counting <command>VACUUM FULL</command>)
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>autovacuum_count</structfield> <type>bigint</type>
@@ -4577,6 +4627,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>lock_skipped_autovacuum_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times vacuums on this table by the autovacuum daemon have been
+ attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>analyze_count</structfield> <type>bigint</type>
@@ -4586,6 +4646,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>lock_skipped_analyze_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times manual analyzes on this table have been attempted but
+ skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>autoanalyze_count</structfield> <type>bigint</type>
@@ -4596,6 +4666,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>lock_skipped_autoanalyze_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times analyzes on this table by the autovacuum daemon have
+ been attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>total_vacuum_time</structfield> <type>double precision</type>
@@ -4651,6 +4731,14 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</tgroup>
</table>
+ <note>
+ <para>
+ When a manual vacuum or analyze on a parent table in an inheritance or
+ partitioning hierarchy is skipped, the statistics are recorded only for
+ the parent table, not for its children.
+ </para>
+ </note>
+
</sect2>
<sect2 id="monitoring-pg-stat-autovacuum-scores-view">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 090281a03dd..d5e127f432d 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -736,13 +736,21 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze,
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
+ pg_stat_get_last_lock_skipped_vacuum_time(C.oid) as last_lock_skipped_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_lock_skipped_autovacuum_time(C.oid) as last_lock_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
+ pg_stat_get_last_lock_skipped_analyze_time(C.oid) as last_lock_skipped_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_lock_skipped_autoanalyze_time(C.oid) as last_lock_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
+ pg_stat_get_lock_skipped_vacuum_count(C.oid) AS lock_skipped_vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_lock_skipped_autovacuum_count(C.oid) AS lock_skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
+ pg_stat_get_lock_skipped_analyze_count(C.oid) AS lock_skipped_analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_lock_skipped_autoanalyze_count(C.oid) AS lock_skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 38539a6fd3d..1b3ad4ee082 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -793,8 +793,26 @@ vacuum_open_relation(Oid relid, RangeVar *relation, uint32 options,
rel = try_relation_open(relid, NoLock);
else
{
+ int flags = 0;
+
rel = NULL;
rel_lock = false;
+
+ if ((options & VACOPT_VACUUM) != 0 && (options & VACOPT_FULL) == 0)
+ {
+ if (AmAutoVacuumWorkerProcess())
+ flags |= PGSTAT_REPORT_SKIPPED_AUTOVACUUM;
+ else
+ flags |= PGSTAT_REPORT_SKIPPED_VACUUM;
+ }
+ if ((options & VACOPT_ANALYZE) != 0)
+ {
+ if (AmAutoVacuumWorkerProcess())
+ flags |= PGSTAT_REPORT_SKIPPED_AUTOANALYZE;
+ else
+ flags |= PGSTAT_REPORT_SKIPPED_ANALYZE;
+ }
+ pgstat_report_skipped_vacuum_analyze(relid, flags);
}
/* if relation is opened, leave */
@@ -930,6 +948,8 @@ expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context,
*/
if (!OidIsValid(relid))
{
+ int flags = 0;
+
if (options & VACOPT_VACUUM)
ereport(WARNING,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
@@ -940,6 +960,17 @@ expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
vrel->relation->relname)));
+
+ /* Get relid for statistics reporting */
+ relid = RangeVarGetRelid(vrel->relation, NoLock, true);
+
+ if ((options & VACOPT_VACUUM) != 0 && (options & VACOPT_FULL) == 0)
+ flags |= PGSTAT_REPORT_SKIPPED_VACUUM;
+ if ((options & VACOPT_ANALYZE) != 0)
+ flags |= PGSTAT_REPORT_SKIPPED_ANALYZE;
+
+ pgstat_report_skipped_vacuum_analyze(relid, flags);
+
return vacrels;
}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index 04f2eb21d0b..c17114aa6b2 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -17,12 +17,15 @@
#include "postgres.h"
+#include "access/htup_details.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
+#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/pgstat_internal.h"
#include "utils/rel.h"
+#include "utils/syscache.h"
#include "utils/timestamp.h"
@@ -367,6 +370,88 @@ pgstat_report_analyze(Relation rel,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during vacuum or/and analyze.
+ */
+void
+pgstat_report_skipped_vacuum_analyze(Oid relid, int flags)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+ HeapTuple classTup;
+ bool isshared;
+
+ if (!pgstat_track_counts || !flags)
+ return;
+
+ classTup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(classTup))
+ return; /* somebody deleted the rel, forget it */
+ isshared = ((Form_pg_class) GETSTRUCT(classTup))->relisshared;
+ ReleaseSysCache(classTup);
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
+ isshared ? InvalidOid : MyDatabaseId,
+ relid, false);
+
+ /*
+ * Re-check whether the relation still exists. Unlike the normal stats
+ * reporting path, we don't hold a relation lock here (the whole point of
+ * SKIP_LOCKED is that we failed to acquire one). Without a lock, a
+ * concurrent DROP TABLE could commit between our earlier syscache lookup
+ * and the stats entry creation above, leaving an orphaned stats entry.
+ *
+ * Accept cache invalidation messages first, so that a DROP that committed
+ * after our earlier check is visible to the syscache lookup below.
+ */
+ AcceptInvalidationMessages();
+ classTup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(classTup))
+ {
+ pgstat_unlock_entry(entry_ref);
+ pgstat_drop_entry(PGSTAT_KIND_RELATION,
+ isshared ? InvalidOid : MyDatabaseId, relid, true);
+ return;
+ }
+ ReleaseSysCache(classTup);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+ if (flags & PGSTAT_REPORT_SKIPPED_VACUUM)
+ {
+ tabentry->last_lock_skipped_vacuum_time = ts;
+ tabentry->lock_skipped_vacuum_count++;
+ }
+ else if (flags & PGSTAT_REPORT_SKIPPED_AUTOVACUUM)
+ {
+ tabentry->last_lock_skipped_autovacuum_time = ts;
+ tabentry->lock_skipped_autovacuum_count++;
+ }
+
+ if (flags & PGSTAT_REPORT_SKIPPED_ANALYZE)
+ {
+ tabentry->last_lock_skipped_analyze_time = ts;
+ tabentry->lock_skipped_analyze_count++;
+ }
+ else if (flags & PGSTAT_REPORT_SKIPPED_AUTOANALYZE)
+ {
+ tabentry->last_lock_skipped_autoanalyze_time = ts;
+ tabentry->lock_skipped_autoanalyze_count++;
+ }
+
+ pgstat_unlock_entry(entry_ref);
+
+ /* see pgstat_report_vacuum() */
+ pgstat_flush_io(false);
+ (void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
+}
+
/*
* count a tuple insertion of n tuples
*/
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 565d0e70768..6a05c3660e2 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,18 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_lock_skipped_analyze_count */
+PG_STAT_GET_RELENTRY_INT64(lock_skipped_analyze_count)
+
+/* pg_stat_get_lock_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(lock_skipped_autoanalyze_count)
+
+/* pg_stat_get_lock_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(lock_skipped_autovacuum_count)
+
+/* pg_stat_get_lock_skipped_vacuum_count */
+PG_STAT_GET_RELENTRY_INT64(lock_skipped_vacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +182,18 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_lock_skipped_analyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_lock_skipped_analyze_time)
+
+/* pg_stat_get_last_lock_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_lock_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_lock_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_lock_skipped_autovacuum_time)
+
+/* pg_stat_get_last_lock_skipped_vacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_lock_skipped_vacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index f8a021987b5..df56444cd98 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5678,6 +5678,38 @@
proargmodes => '{o,o,o,o,o,o,o,o,o,o}',
proargnames => '{oid,score,xid_score,mxid_score,vacuum_score,vacuum_insert_score,analyze_score,do_vacuum,do_analyze,for_wraparound}',
prosrc => 'pg_stat_get_autovacuum_scores' },
+{ oid => '8142', descr => 'statistics: last skipped vacuum time for a table',
+ proname => 'pg_stat_get_last_lock_skipped_vacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_lock_skipped_vacuum_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_lock_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_lock_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: last skipped analyze time for a table',
+ proname => 'pg_stat_get_last_lock_skipped_analyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_lock_skipped_analyze_time' },
+{ oid => '8145', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_lock_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_lock_skipped_autoanalyze_time' },
+{ oid => '8146', descr => 'statistics: number of skipped vacuum for a table',
+ proname => 'pg_stat_get_lock_skipped_vacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_lock_skipped_vacuum_count' },
+{ oid => '8147', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_lock_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_lock_skipped_autovacuum_count' },
+{ oid => '8148', descr => 'statistics: number of skipped analyzes for a table',
+ proname => 'pg_stat_get_lock_skipped_analyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_lock_skipped_analyze_count' },
+{ oid => '8149', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_lock_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_lock_skipped_autoanalyze_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 58a44857f13..249db7ffd0b 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -218,7 +218,7 @@ typedef struct PgStat_TableXactStatus
* ------------------------------------------------------------
*/
-#define PGSTAT_FILE_FORMAT_ID 0x01A5BCBC
+#define PGSTAT_FILE_FORMAT_ID 0x01A5BCBD
typedef struct PgStat_ArchiverStats
{
@@ -479,6 +479,15 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_lock_skipped_vacuum_time; /* user initiated vacuum */
+ PgStat_Counter lock_skipped_vacuum_count;
+ TimestampTz last_lock_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter lock_skipped_autovacuum_count;
+ TimestampTz last_lock_skipped_analyze_time; /* user initiated */
+ PgStat_Counter lock_skipped_analyze_count;
+ TimestampTz last_lock_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter lock_skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -716,6 +725,14 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+/* flags for pgstat_flush_backend() */
+#define PGSTAT_REPORT_SKIPPED_VACUUM (1 << 0) /* vacuum is skipped */
+#define PGSTAT_REPORT_SKIPPED_ANALYZE (1 << 1) /* analyze is skipped */
+#define PGSTAT_REPORT_SKIPPED_AUTOVACUUM (1 << 2) /* autovacuum is skipped */
+#define PGSTAT_REPORT_SKIPPED_AUTOANALYZE (1 << 3) /* autoanalyze is
+ * skipped */
+extern void pgstat_report_skipped_vacuum_analyze(Oid relid, int flags);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/isolation/expected/vacuum-skip-locked.out b/src/test/isolation/expected/vacuum-skip-locked.out
index 99db281a159..73a133ad9de 100644
--- a/src/test/isolation/expected/vacuum-skip-locked.out
+++ b/src/test/isolation/expected/vacuum-skip-locked.out
@@ -1,6 +1,6 @@
Parsed test spec with 2 sessions
-starting permutation: lock_share vac_specified commit
+starting permutation: lock_share vac_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -10,8 +10,22 @@ step vac_specified: VACUUM (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_all_parts commit
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -20,8 +34,22 @@ step vac_all_parts: VACUUM (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
+
-starting permutation: lock_share analyze_specified commit
+starting permutation: lock_share analyze_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -31,8 +59,22 @@ step analyze_specified: ANALYZE (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share analyze_all_parts commit
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_share analyze_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -41,8 +83,22 @@ step analyze_all_parts: ANALYZE (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 1| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_share vac_analyze_specified commit
+starting permutation: lock_share vac_analyze_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -52,8 +108,22 @@ step vac_analyze_specified: VACUUM (ANALYZE, SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_analyze_all_parts commit
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_analyze_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -62,8 +132,22 @@ step vac_analyze_all_parts: VACUUM (ANALYZE, SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 1| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_share vac_full_specified commit
+starting permutation: lock_share vac_full_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -73,8 +157,22 @@ step vac_full_specified: VACUUM (SKIP_LOCKED, FULL) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_full_all_parts commit
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_full_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -83,8 +181,22 @@ step vac_full_all_parts: VACUUM (SKIP_LOCKED, FULL) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_specified commit
+
+starting permutation: lock_access_exclusive vac_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -94,8 +206,22 @@ step vac_specified: VACUUM (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_all_parts commit
+
+starting permutation: lock_access_exclusive vac_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -104,8 +230,22 @@ step vac_all_parts: VACUUM (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive analyze_specified commit
+
+starting permutation: lock_access_exclusive analyze_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -115,8 +255,22 @@ step analyze_specified: ANALYZE (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_access_exclusive analyze_all_parts commit
+starting permutation: lock_access_exclusive analyze_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -126,8 +280,22 @@ step commit:
COMMIT;
step analyze_all_parts: <... completed>
-
-starting permutation: lock_access_exclusive vac_analyze_specified commit
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 1| 0
+part1 | 0| 0| 1| 0
+part2 | 0| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_analyze_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -137,8 +305,22 @@ step vac_analyze_specified: VACUUM (ANALYZE, SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_analyze_all_parts commit
+
+starting permutation: lock_access_exclusive vac_analyze_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -148,8 +330,22 @@ step commit:
COMMIT;
step vac_analyze_all_parts: <... completed>
-
-starting permutation: lock_access_exclusive vac_full_specified commit
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 1| 0
+part1 | 1| 0| 1| 0
+part2 | 1| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_full_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -159,8 +355,22 @@ step vac_full_specified: VACUUM (SKIP_LOCKED, FULL) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_access_exclusive vac_full_all_parts commit
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_full_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -169,3 +379,17 @@ step vac_full_all_parts: VACUUM (SKIP_LOCKED, FULL) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
diff --git a/src/test/isolation/specs/vacuum-skip-locked.spec b/src/test/isolation/specs/vacuum-skip-locked.spec
index 3fad6e1c92a..5b104439404 100644
--- a/src/test/isolation/specs/vacuum-skip-locked.spec
+++ b/src/test/isolation/specs/vacuum-skip-locked.spec
@@ -33,6 +33,15 @@ step commit
COMMIT;
}
+step check_stat
+{
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+}
+
session s2
step vac_specified { VACUUM (SKIP_LOCKED) part1, part2; }
step vac_all_parts { VACUUM (SKIP_LOCKED) parted; }
@@ -43,19 +52,19 @@ step vac_analyze_all_parts { VACUUM (ANALYZE, SKIP_LOCKED) parted; }
step vac_full_specified { VACUUM (SKIP_LOCKED, FULL) part1, part2; }
step vac_full_all_parts { VACUUM (SKIP_LOCKED, FULL) parted; }
-permutation lock_share vac_specified commit
-permutation lock_share vac_all_parts commit
-permutation lock_share analyze_specified commit
-permutation lock_share analyze_all_parts commit
-permutation lock_share vac_analyze_specified commit
-permutation lock_share vac_analyze_all_parts commit
-permutation lock_share vac_full_specified commit
-permutation lock_share vac_full_all_parts commit
-permutation lock_access_exclusive vac_specified commit
-permutation lock_access_exclusive vac_all_parts commit
-permutation lock_access_exclusive analyze_specified commit
-permutation lock_access_exclusive analyze_all_parts commit
-permutation lock_access_exclusive vac_analyze_specified commit
-permutation lock_access_exclusive vac_analyze_all_parts commit
-permutation lock_access_exclusive vac_full_specified commit
-permutation lock_access_exclusive vac_full_all_parts commit
+permutation lock_share vac_specified commit check_stat
+permutation lock_share vac_all_parts commit check_stat
+permutation lock_share analyze_specified commit check_stat
+permutation lock_share analyze_all_parts commit check_stat
+permutation lock_share vac_analyze_specified commit check_stat
+permutation lock_share vac_analyze_all_parts commit check_stat
+permutation lock_share vac_full_specified commit check_stat
+permutation lock_share vac_full_all_parts commit check_stat
+permutation lock_access_exclusive vac_specified commit check_stat
+permutation lock_access_exclusive vac_all_parts commit check_stat
+permutation lock_access_exclusive analyze_specified commit check_stat
+permutation lock_access_exclusive analyze_all_parts commit check_stat
+permutation lock_access_exclusive vac_analyze_specified commit check_stat
+permutation lock_access_exclusive vac_analyze_all_parts commit check_stat
+permutation lock_access_exclusive vac_full_specified commit check_stat
+permutation lock_access_exclusive vac_full_all_parts commit check_stat
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 6a3341356da..3fcee4dc097 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1835,13 +1835,21 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
+ pg_stat_get_last_lock_skipped_vacuum_time(c.oid) AS last_lock_skipped_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_lock_skipped_autovacuum_time(c.oid) AS last_lock_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
+ pg_stat_get_last_lock_skipped_analyze_time(c.oid) AS last_lock_skipped_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_lock_skipped_autoanalyze_time(c.oid) AS last_lock_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
+ pg_stat_get_lock_skipped_vacuum_count(c.oid) AS lock_skipped_vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_lock_skipped_autovacuum_count(c.oid) AS lock_skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
+ pg_stat_get_lock_skipped_analyze_count(c.oid) AS lock_skipped_analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_lock_skipped_autoanalyze_count(c.oid) AS lock_skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2357,13 +2365,21 @@ pg_stat_sys_tables| SELECT relid,
n_mod_since_analyze,
n_ins_since_vacuum,
last_vacuum,
+ last_lock_skipped_vacuum,
last_autovacuum,
+ last_lock_skipped_autovacuum,
last_analyze,
+ last_lock_skipped_analyze,
last_autoanalyze,
+ last_lock_skipped_autoanalyze,
vacuum_count,
+ lock_skipped_vacuum_count,
autovacuum_count,
+ lock_skipped_autovacuum_count,
analyze_count,
+ lock_skipped_analyze_count,
autoanalyze_count,
+ lock_skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2412,13 +2428,21 @@ pg_stat_user_tables| SELECT relid,
n_mod_since_analyze,
n_ins_since_vacuum,
last_vacuum,
+ last_lock_skipped_vacuum,
last_autovacuum,
+ last_lock_skipped_autovacuum,
last_analyze,
+ last_lock_skipped_analyze,
last_autoanalyze,
+ last_lock_skipped_autoanalyze,
vacuum_count,
+ lock_skipped_vacuum_count,
autovacuum_count,
+ lock_skipped_autovacuum_count,
analyze_count,
+ lock_skipped_analyze_count,
autoanalyze_count,
+ lock_skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 05:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-15 09:02 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 16:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 20:21 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-16 14:29 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-11 08:23 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-21 18:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
@ 2026-07-21 18:51 ` Sami Imseih <samimseih@gmail.com>
2026-07-22 04:20 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
0 siblings, 1 reply; 376+ messages in thread
From: Sami Imseih @ 2026-07-21 18:51 UTC (permalink / raw)
To: Shinya Kato <shinya11.kato@gmail.com>; +Cc: Zsolt Parragi <zsolt.parragi@percona.com>; pgsql-hackers@lists.postgresql.org
Sorry, I missed attaching the injection point test.
v11 includes it.
--
Sami
Attachments:
[application/octet-stream] v11-0001-Track-skipped-vacuum-and-analyze-activity-per-re.patch (42.3K, ../../CAA5RZ0spPHH-SAJsdn5AB8EQcdTQ53hULMSg2e0qufh9=g-2gg@mail.gmail.com/2-v11-0001-Track-skipped-vacuum-and-analyze-activity-per-re.patch)
download | inline diff:
From 848d3e1065c2fb3aa1c64e80c18c4dad6c4369a5 Mon Sep 17 00:00:00 2001
From: Yugo Nagata <nagata@sraoss.co.jp>
Date: Tue, 24 Mar 2026 13:09:00 +0900
Subject: [PATCH v11 1/2] Track skipped vacuum and analyze activity per
relation
This commit adds eight fields to the relation statistics that track
the last time vacuum or analyze has been attempted but skipped due to
lock unavailability, along with their counts:
- last_lock_skipped_vacuum
- last_lock_skipped_autovacuum
- last_lock_skipped_analyze
- last_lock_skipped_autoanalyze
- lock_skipped_vacuum_count
- lock_skipped_autovacuum_count
- lock_skipped_analyze_count
- lock_skipped_autoanalyze_count
The field names make explicit that the skip is due to lock
unavailability, distinguishing it from other reasons a vacuum or
analyze might not run on a relation.
These fields can help users confirm that autovacuum is actively attempting
to run on a table that has not been vacuumed or analyzed for a long time,
and that the lack of progress is due to repeated skips rather than inactivity.
---
doc/src/sgml/monitoring.sgml | 88 ++++++
src/backend/catalog/system_views.sql | 8 +
src/backend/commands/vacuum.c | 31 +++
src/backend/utils/activity/pgstat_relation.c | 85 ++++++
src/backend/utils/adt/pgstatfuncs.c | 24 ++
src/include/catalog/pg_proc.dat | 32 +++
src/include/pgstat.h | 19 +-
.../isolation/expected/vacuum-skip-locked.out | 260 ++++++++++++++++--
.../isolation/specs/vacuum-skip-locked.spec | 41 +--
src/test/regress/expected/rules.out | 24 ++
10 files changed, 577 insertions(+), 35 deletions(-)
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index d1a20d001e9..2912ec975d6 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4528,6 +4528,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_lock_skipped_vacuum</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a manual vacuum on this table was attempted but skipped due to
+ lock unavailability (not counting <command>VACUUM FULL</command>)
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_autovacuum</structfield> <type>timestamp with time zone</type>
@@ -4538,6 +4548,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_lock_skipped_autovacuum</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a vacuum on this table by the autovacuum daemon was attempted
+ but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_analyze</structfield> <type>timestamp with time zone</type>
@@ -4547,6 +4567,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_lock_skipped_analyze</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time a manual analyze on this table was attempted but skipped due to
+ lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>last_autoanalyze</structfield> <type>timestamp with time zone</type>
@@ -4557,6 +4587,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>last_lock_skipped_autoanalyze</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Last time at which an analyze on this table by the autovacuum was
+ attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>vacuum_count</structfield> <type>bigint</type>
@@ -4567,6 +4607,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>lock_skipped_vacuum_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times manual vacuums on this table have been attempted but skipped
+ due to lock unavailability (not counting <command>VACUUM FULL</command>)
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>autovacuum_count</structfield> <type>bigint</type>
@@ -4577,6 +4627,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>lock_skipped_autovacuum_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times vacuums on this table by the autovacuum daemon have been
+ attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>analyze_count</structfield> <type>bigint</type>
@@ -4586,6 +4646,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>lock_skipped_analyze_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times manual analyzes on this table have been attempted but
+ skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>autoanalyze_count</structfield> <type>bigint</type>
@@ -4596,6 +4666,16 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>lock_skipped_autoanalyze_count</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times analyzes on this table by the autovacuum daemon have
+ been attempted but skipped due to lock unavailability
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>total_vacuum_time</structfield> <type>double precision</type>
@@ -4651,6 +4731,14 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</tgroup>
</table>
+ <note>
+ <para>
+ When a manual vacuum or analyze on a parent table in an inheritance or
+ partitioning hierarchy is skipped, the statistics are recorded only for
+ the parent table, not for its children.
+ </para>
+ </note>
+
</sect2>
<sect2 id="monitoring-pg-stat-autovacuum-scores-view">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 090281a03dd..d5e127f432d 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -736,13 +736,21 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze,
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
+ pg_stat_get_last_lock_skipped_vacuum_time(C.oid) as last_lock_skipped_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_lock_skipped_autovacuum_time(C.oid) as last_lock_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
+ pg_stat_get_last_lock_skipped_analyze_time(C.oid) as last_lock_skipped_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_lock_skipped_autoanalyze_time(C.oid) as last_lock_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
+ pg_stat_get_lock_skipped_vacuum_count(C.oid) AS lock_skipped_vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_lock_skipped_autovacuum_count(C.oid) AS lock_skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
+ pg_stat_get_lock_skipped_analyze_count(C.oid) AS lock_skipped_analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_lock_skipped_autoanalyze_count(C.oid) AS lock_skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 38539a6fd3d..1b3ad4ee082 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -793,8 +793,26 @@ vacuum_open_relation(Oid relid, RangeVar *relation, uint32 options,
rel = try_relation_open(relid, NoLock);
else
{
+ int flags = 0;
+
rel = NULL;
rel_lock = false;
+
+ if ((options & VACOPT_VACUUM) != 0 && (options & VACOPT_FULL) == 0)
+ {
+ if (AmAutoVacuumWorkerProcess())
+ flags |= PGSTAT_REPORT_SKIPPED_AUTOVACUUM;
+ else
+ flags |= PGSTAT_REPORT_SKIPPED_VACUUM;
+ }
+ if ((options & VACOPT_ANALYZE) != 0)
+ {
+ if (AmAutoVacuumWorkerProcess())
+ flags |= PGSTAT_REPORT_SKIPPED_AUTOANALYZE;
+ else
+ flags |= PGSTAT_REPORT_SKIPPED_ANALYZE;
+ }
+ pgstat_report_skipped_vacuum_analyze(relid, flags);
}
/* if relation is opened, leave */
@@ -930,6 +948,8 @@ expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context,
*/
if (!OidIsValid(relid))
{
+ int flags = 0;
+
if (options & VACOPT_VACUUM)
ereport(WARNING,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
@@ -940,6 +960,17 @@ expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
vrel->relation->relname)));
+
+ /* Get relid for statistics reporting */
+ relid = RangeVarGetRelid(vrel->relation, NoLock, true);
+
+ if ((options & VACOPT_VACUUM) != 0 && (options & VACOPT_FULL) == 0)
+ flags |= PGSTAT_REPORT_SKIPPED_VACUUM;
+ if ((options & VACOPT_ANALYZE) != 0)
+ flags |= PGSTAT_REPORT_SKIPPED_ANALYZE;
+
+ pgstat_report_skipped_vacuum_analyze(relid, flags);
+
return vacrels;
}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index 04f2eb21d0b..c17114aa6b2 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -17,12 +17,15 @@
#include "postgres.h"
+#include "access/htup_details.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
+#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/pgstat_internal.h"
#include "utils/rel.h"
+#include "utils/syscache.h"
#include "utils/timestamp.h"
@@ -367,6 +370,88 @@ pgstat_report_analyze(Relation rel,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during vacuum or/and analyze.
+ */
+void
+pgstat_report_skipped_vacuum_analyze(Oid relid, int flags)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+ HeapTuple classTup;
+ bool isshared;
+
+ if (!pgstat_track_counts || !flags)
+ return;
+
+ classTup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(classTup))
+ return; /* somebody deleted the rel, forget it */
+ isshared = ((Form_pg_class) GETSTRUCT(classTup))->relisshared;
+ ReleaseSysCache(classTup);
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
+ isshared ? InvalidOid : MyDatabaseId,
+ relid, false);
+
+ /*
+ * Re-check whether the relation still exists. Unlike the normal stats
+ * reporting path, we don't hold a relation lock here (the whole point of
+ * SKIP_LOCKED is that we failed to acquire one). Without a lock, a
+ * concurrent DROP TABLE could commit between our earlier syscache lookup
+ * and the stats entry creation above, leaving an orphaned stats entry.
+ *
+ * Accept cache invalidation messages first, so that a DROP that committed
+ * after our earlier check is visible to the syscache lookup below.
+ */
+ AcceptInvalidationMessages();
+ classTup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(classTup))
+ {
+ pgstat_unlock_entry(entry_ref);
+ pgstat_drop_entry(PGSTAT_KIND_RELATION,
+ isshared ? InvalidOid : MyDatabaseId, relid, true);
+ return;
+ }
+ ReleaseSysCache(classTup);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+ if (flags & PGSTAT_REPORT_SKIPPED_VACUUM)
+ {
+ tabentry->last_lock_skipped_vacuum_time = ts;
+ tabentry->lock_skipped_vacuum_count++;
+ }
+ else if (flags & PGSTAT_REPORT_SKIPPED_AUTOVACUUM)
+ {
+ tabentry->last_lock_skipped_autovacuum_time = ts;
+ tabentry->lock_skipped_autovacuum_count++;
+ }
+
+ if (flags & PGSTAT_REPORT_SKIPPED_ANALYZE)
+ {
+ tabentry->last_lock_skipped_analyze_time = ts;
+ tabentry->lock_skipped_analyze_count++;
+ }
+ else if (flags & PGSTAT_REPORT_SKIPPED_AUTOANALYZE)
+ {
+ tabentry->last_lock_skipped_autoanalyze_time = ts;
+ tabentry->lock_skipped_autoanalyze_count++;
+ }
+
+ pgstat_unlock_entry(entry_ref);
+
+ /* see pgstat_report_vacuum() */
+ pgstat_flush_io(false);
+ (void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
+}
+
/*
* count a tuple insertion of n tuples
*/
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 565d0e70768..6a05c3660e2 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,18 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_lock_skipped_analyze_count */
+PG_STAT_GET_RELENTRY_INT64(lock_skipped_analyze_count)
+
+/* pg_stat_get_lock_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(lock_skipped_autoanalyze_count)
+
+/* pg_stat_get_lock_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(lock_skipped_autovacuum_count)
+
+/* pg_stat_get_lock_skipped_vacuum_count */
+PG_STAT_GET_RELENTRY_INT64(lock_skipped_vacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +182,18 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_lock_skipped_analyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_lock_skipped_analyze_time)
+
+/* pg_stat_get_last_lock_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_lock_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_lock_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_lock_skipped_autovacuum_time)
+
+/* pg_stat_get_last_lock_skipped_vacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_lock_skipped_vacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index f8a021987b5..df56444cd98 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5678,6 +5678,38 @@
proargmodes => '{o,o,o,o,o,o,o,o,o,o}',
proargnames => '{oid,score,xid_score,mxid_score,vacuum_score,vacuum_insert_score,analyze_score,do_vacuum,do_analyze,for_wraparound}',
prosrc => 'pg_stat_get_autovacuum_scores' },
+{ oid => '8142', descr => 'statistics: last skipped vacuum time for a table',
+ proname => 'pg_stat_get_last_lock_skipped_vacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_lock_skipped_vacuum_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_lock_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_lock_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: last skipped analyze time for a table',
+ proname => 'pg_stat_get_last_lock_skipped_analyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_lock_skipped_analyze_time' },
+{ oid => '8145', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_lock_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_lock_skipped_autoanalyze_time' },
+{ oid => '8146', descr => 'statistics: number of skipped vacuum for a table',
+ proname => 'pg_stat_get_lock_skipped_vacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_lock_skipped_vacuum_count' },
+{ oid => '8147', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_lock_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_lock_skipped_autovacuum_count' },
+{ oid => '8148', descr => 'statistics: number of skipped analyzes for a table',
+ proname => 'pg_stat_get_lock_skipped_analyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_lock_skipped_analyze_count' },
+{ oid => '8149', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_lock_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_lock_skipped_autoanalyze_count' },
{ oid => '1936', descr => 'statistics: currently active backend IDs',
proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 58a44857f13..249db7ffd0b 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -218,7 +218,7 @@ typedef struct PgStat_TableXactStatus
* ------------------------------------------------------------
*/
-#define PGSTAT_FILE_FORMAT_ID 0x01A5BCBC
+#define PGSTAT_FILE_FORMAT_ID 0x01A5BCBD
typedef struct PgStat_ArchiverStats
{
@@ -479,6 +479,15 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_lock_skipped_vacuum_time; /* user initiated vacuum */
+ PgStat_Counter lock_skipped_vacuum_count;
+ TimestampTz last_lock_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter lock_skipped_autovacuum_count;
+ TimestampTz last_lock_skipped_analyze_time; /* user initiated */
+ PgStat_Counter lock_skipped_analyze_count;
+ TimestampTz last_lock_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter lock_skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -716,6 +725,14 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+/* flags for pgstat_flush_backend() */
+#define PGSTAT_REPORT_SKIPPED_VACUUM (1 << 0) /* vacuum is skipped */
+#define PGSTAT_REPORT_SKIPPED_ANALYZE (1 << 1) /* analyze is skipped */
+#define PGSTAT_REPORT_SKIPPED_AUTOVACUUM (1 << 2) /* autovacuum is skipped */
+#define PGSTAT_REPORT_SKIPPED_AUTOANALYZE (1 << 3) /* autoanalyze is
+ * skipped */
+extern void pgstat_report_skipped_vacuum_analyze(Oid relid, int flags);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/isolation/expected/vacuum-skip-locked.out b/src/test/isolation/expected/vacuum-skip-locked.out
index 99db281a159..73a133ad9de 100644
--- a/src/test/isolation/expected/vacuum-skip-locked.out
+++ b/src/test/isolation/expected/vacuum-skip-locked.out
@@ -1,6 +1,6 @@
Parsed test spec with 2 sessions
-starting permutation: lock_share vac_specified commit
+starting permutation: lock_share vac_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -10,8 +10,22 @@ step vac_specified: VACUUM (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_all_parts commit
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -20,8 +34,22 @@ step vac_all_parts: VACUUM (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
+
-starting permutation: lock_share analyze_specified commit
+starting permutation: lock_share analyze_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -31,8 +59,22 @@ step analyze_specified: ANALYZE (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share analyze_all_parts commit
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_share analyze_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -41,8 +83,22 @@ step analyze_all_parts: ANALYZE (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 1| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_share vac_analyze_specified commit
+starting permutation: lock_share vac_analyze_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -52,8 +108,22 @@ step vac_analyze_specified: VACUUM (ANALYZE, SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_analyze_all_parts commit
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_analyze_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -62,8 +132,22 @@ step vac_analyze_all_parts: VACUUM (ANALYZE, SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 1| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_share vac_full_specified commit
+starting permutation: lock_share vac_full_specified commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -73,8 +157,22 @@ step vac_full_specified: VACUUM (SKIP_LOCKED, FULL) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_share vac_full_all_parts commit
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_share vac_full_all_parts commit check_stat
step lock_share:
BEGIN;
LOCK part1 IN SHARE MODE;
@@ -83,8 +181,22 @@ step vac_full_all_parts: VACUUM (SKIP_LOCKED, FULL) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_specified commit
+
+starting permutation: lock_access_exclusive vac_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -94,8 +206,22 @@ step vac_specified: VACUUM (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_all_parts commit
+
+starting permutation: lock_access_exclusive vac_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -104,8 +230,22 @@ step vac_all_parts: VACUUM (SKIP_LOCKED) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 0
+part2 | 1| 0| 0| 0
+(3 rows)
-starting permutation: lock_access_exclusive analyze_specified commit
+
+starting permutation: lock_access_exclusive analyze_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -115,8 +255,22 @@ step analyze_specified: ANALYZE (SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 1
+part2 | 0| 0| 1| 0
+(3 rows)
+
-starting permutation: lock_access_exclusive analyze_all_parts commit
+starting permutation: lock_access_exclusive analyze_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -126,8 +280,22 @@ step commit:
COMMIT;
step analyze_all_parts: <... completed>
-
-starting permutation: lock_access_exclusive vac_analyze_specified commit
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 1| 0
+part1 | 0| 0| 1| 0
+part2 | 0| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_analyze_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -137,8 +305,22 @@ step vac_analyze_specified: VACUUM (ANALYZE, SKIP_LOCKED) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 1| 0| 1
+part2 | 1| 0| 1| 0
+(3 rows)
-starting permutation: lock_access_exclusive vac_analyze_all_parts commit
+
+starting permutation: lock_access_exclusive vac_analyze_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -148,8 +330,22 @@ step commit:
COMMIT;
step vac_analyze_all_parts: <... completed>
-
-starting permutation: lock_access_exclusive vac_full_specified commit
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 1| 0
+part1 | 1| 0| 1| 0
+part2 | 1| 0| 1| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_full_specified commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -159,8 +355,22 @@ step vac_full_specified: VACUUM (SKIP_LOCKED, FULL) part1, part2;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
-starting permutation: lock_access_exclusive vac_full_all_parts commit
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
+
+starting permutation: lock_access_exclusive vac_full_all_parts commit check_stat
step lock_access_exclusive:
BEGIN;
LOCK part1 IN ACCESS EXCLUSIVE MODE;
@@ -169,3 +379,17 @@ step vac_full_all_parts: VACUUM (SKIP_LOCKED, FULL) parted;
step commit:
COMMIT;
+step check_stat:
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+
+relname|vacuum_count|lock_skipped_vacuum_count|analyze_count|lock_skipped_analyze_count
+-------+------------+-------------------------+-------------+--------------------------
+parted | 0| 0| 0| 0
+part1 | 0| 0| 0| 0
+part2 | 0| 0| 0| 0
+(3 rows)
+
diff --git a/src/test/isolation/specs/vacuum-skip-locked.spec b/src/test/isolation/specs/vacuum-skip-locked.spec
index 3fad6e1c92a..5b104439404 100644
--- a/src/test/isolation/specs/vacuum-skip-locked.spec
+++ b/src/test/isolation/specs/vacuum-skip-locked.spec
@@ -33,6 +33,15 @@ step commit
COMMIT;
}
+step check_stat
+{
+ SELECT relname,
+ vacuum_count, lock_skipped_vacuum_count,
+ analyze_count, lock_skipped_analyze_count
+ FROM pg_stat_all_tables
+ WHERE relname IN ('parted', 'part1', 'part2');
+}
+
session s2
step vac_specified { VACUUM (SKIP_LOCKED) part1, part2; }
step vac_all_parts { VACUUM (SKIP_LOCKED) parted; }
@@ -43,19 +52,19 @@ step vac_analyze_all_parts { VACUUM (ANALYZE, SKIP_LOCKED) parted; }
step vac_full_specified { VACUUM (SKIP_LOCKED, FULL) part1, part2; }
step vac_full_all_parts { VACUUM (SKIP_LOCKED, FULL) parted; }
-permutation lock_share vac_specified commit
-permutation lock_share vac_all_parts commit
-permutation lock_share analyze_specified commit
-permutation lock_share analyze_all_parts commit
-permutation lock_share vac_analyze_specified commit
-permutation lock_share vac_analyze_all_parts commit
-permutation lock_share vac_full_specified commit
-permutation lock_share vac_full_all_parts commit
-permutation lock_access_exclusive vac_specified commit
-permutation lock_access_exclusive vac_all_parts commit
-permutation lock_access_exclusive analyze_specified commit
-permutation lock_access_exclusive analyze_all_parts commit
-permutation lock_access_exclusive vac_analyze_specified commit
-permutation lock_access_exclusive vac_analyze_all_parts commit
-permutation lock_access_exclusive vac_full_specified commit
-permutation lock_access_exclusive vac_full_all_parts commit
+permutation lock_share vac_specified commit check_stat
+permutation lock_share vac_all_parts commit check_stat
+permutation lock_share analyze_specified commit check_stat
+permutation lock_share analyze_all_parts commit check_stat
+permutation lock_share vac_analyze_specified commit check_stat
+permutation lock_share vac_analyze_all_parts commit check_stat
+permutation lock_share vac_full_specified commit check_stat
+permutation lock_share vac_full_all_parts commit check_stat
+permutation lock_access_exclusive vac_specified commit check_stat
+permutation lock_access_exclusive vac_all_parts commit check_stat
+permutation lock_access_exclusive analyze_specified commit check_stat
+permutation lock_access_exclusive analyze_all_parts commit check_stat
+permutation lock_access_exclusive vac_analyze_specified commit check_stat
+permutation lock_access_exclusive vac_analyze_all_parts commit check_stat
+permutation lock_access_exclusive vac_full_specified commit check_stat
+permutation lock_access_exclusive vac_full_all_parts commit check_stat
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 6a3341356da..3fcee4dc097 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1835,13 +1835,21 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
+ pg_stat_get_last_lock_skipped_vacuum_time(c.oid) AS last_lock_skipped_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_lock_skipped_autovacuum_time(c.oid) AS last_lock_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
+ pg_stat_get_last_lock_skipped_analyze_time(c.oid) AS last_lock_skipped_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_lock_skipped_autoanalyze_time(c.oid) AS last_lock_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
+ pg_stat_get_lock_skipped_vacuum_count(c.oid) AS lock_skipped_vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_lock_skipped_autovacuum_count(c.oid) AS lock_skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
+ pg_stat_get_lock_skipped_analyze_count(c.oid) AS lock_skipped_analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_lock_skipped_autoanalyze_count(c.oid) AS lock_skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2357,13 +2365,21 @@ pg_stat_sys_tables| SELECT relid,
n_mod_since_analyze,
n_ins_since_vacuum,
last_vacuum,
+ last_lock_skipped_vacuum,
last_autovacuum,
+ last_lock_skipped_autovacuum,
last_analyze,
+ last_lock_skipped_analyze,
last_autoanalyze,
+ last_lock_skipped_autoanalyze,
vacuum_count,
+ lock_skipped_vacuum_count,
autovacuum_count,
+ lock_skipped_autovacuum_count,
analyze_count,
+ lock_skipped_analyze_count,
autoanalyze_count,
+ lock_skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2412,13 +2428,21 @@ pg_stat_user_tables| SELECT relid,
n_mod_since_analyze,
n_ins_since_vacuum,
last_vacuum,
+ last_lock_skipped_vacuum,
last_autovacuum,
+ last_lock_skipped_autovacuum,
last_analyze,
+ last_lock_skipped_analyze,
last_autoanalyze,
+ last_lock_skipped_autoanalyze,
vacuum_count,
+ lock_skipped_vacuum_count,
autovacuum_count,
+ lock_skipped_autovacuum_count,
analyze_count,
+ lock_skipped_analyze_count,
autoanalyze_count,
+ lock_skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.50.1 (Apple Git-155)
[application/octet-stream] v11-0002-Add-injection-point-test-for-vacuum-skip_locked-.patch (7.3K, ../../CAA5RZ0spPHH-SAJsdn5AB8EQcdTQ53hULMSg2e0qufh9=g-2gg@mail.gmail.com/3-v11-0002-Add-injection-point-test-for-vacuum-skip_locked-.patch)
download | inline diff:
From 4413618120c04d990a0229bd7d623ac10bb2b628 Mon Sep 17 00:00:00 2001
From: Sami Imseih <samimseih@gmail.com>
Date: Fri, 15 May 2026 09:07:40 -0500
Subject: [PATCH v11 2/2] Add injection point test for vacuum skip_locked stats
Add an isolation test exercising the race window between VACUUM
(SKIP_LOCKED) reporting a skipped vacuum and concurrent table drops.
Two scenarios are tested:
1. Table dropped (committed) while vacuumer is blocked at the injection
point: no orphaned stats entry is created.
2. DROP TABLE rolled back while vacuumer is blocked: skip is still
recorded since the table and its stats entry survive.
---
src/backend/utils/activity/pgstat_relation.c | 2 +
.../expected/vacuum_skip_lock_stats.out | 91 +++++++++++++++++++
src/test/modules/injection_points/meson.build | 1 +
.../specs/vacuum_skip_lock_stats.spec | 67 ++++++++++++++
4 files changed, 161 insertions(+)
create mode 100644 src/test/modules/injection_points/expected/vacuum_skip_lock_stats.out
create mode 100644 src/test/modules/injection_points/specs/vacuum_skip_lock_stats.spec
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index c17114aa6b2..0aa59e7cf8d 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -21,6 +21,7 @@
#include "access/twophase_rmgr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
+#include "utils/injection_point.h"
#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/pgstat_internal.h"
@@ -391,6 +392,7 @@ pgstat_report_skipped_vacuum_analyze(Oid relid, int flags)
return; /* somebody deleted the rel, forget it */
isshared = ((Form_pg_class) GETSTRUCT(classTup))->relisshared;
ReleaseSysCache(classTup);
+ INJECTION_POINT("skipped-vacuum-analyze-before-entry-lock", NULL);
/* Store the data in the table's hash table entry. */
ts = GetCurrentTimestamp();
diff --git a/src/test/modules/injection_points/expected/vacuum_skip_lock_stats.out b/src/test/modules/injection_points/expected/vacuum_skip_lock_stats.out
new file mode 100644
index 00000000000..4d4aeeef9fa
--- /dev/null
+++ b/src/test/modules/injection_points/expected/vacuum_skip_lock_stats.out
@@ -0,0 +1,91 @@
+Parsed test spec with 3 sessions
+
+starting permutation: lock vacuum unlock drop_table wakeup check_stats detach
+injection_points_attach
+-----------------------
+
+(1 row)
+
+step lock:
+ BEGIN;
+ LOCK TABLE test_skip IN ACCESS EXCLUSIVE MODE;
+
+s2: WARNING: skipping vacuum of "test_skip" --- lock not available
+step vacuum: VACUUM (SKIP_LOCKED) test_skip; <waiting ...>
+step unlock: COMMIT;
+step drop_table: DROP TABLE test_skip;
+step wakeup: SELECT injection_points_wakeup('skipped-vacuum-analyze-before-entry-lock');
+injection_points_wakeup
+-----------------------
+
+(1 row)
+
+step vacuum: <... completed>
+step check_stats:
+ SELECT pg_stat_force_next_flush();
+ SELECT pg_stat_get_lock_skipped_vacuum_count(oid_val) AS skip_count
+ FROM saved_oid;
+
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+skip_count
+----------
+ 0
+(1 row)
+
+step detach: SELECT injection_points_detach('skipped-vacuum-analyze-before-entry-lock');
+injection_points_detach
+-----------------------
+
+(1 row)
+
+
+starting permutation: lock vacuum unlock rollback_drop wakeup check_stats detach
+injection_points_attach
+-----------------------
+
+(1 row)
+
+step lock:
+ BEGIN;
+ LOCK TABLE test_skip IN ACCESS EXCLUSIVE MODE;
+
+s2: WARNING: skipping vacuum of "test_skip" --- lock not available
+step vacuum: VACUUM (SKIP_LOCKED) test_skip; <waiting ...>
+step unlock: COMMIT;
+step rollback_drop:
+ BEGIN;
+ DROP TABLE test_skip;
+ ROLLBACK;
+
+step wakeup: SELECT injection_points_wakeup('skipped-vacuum-analyze-before-entry-lock');
+injection_points_wakeup
+-----------------------
+
+(1 row)
+
+step vacuum: <... completed>
+step check_stats:
+ SELECT pg_stat_force_next_flush();
+ SELECT pg_stat_get_lock_skipped_vacuum_count(oid_val) AS skip_count
+ FROM saved_oid;
+
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+skip_count
+----------
+ 1
+(1 row)
+
+step detach: SELECT injection_points_detach('skipped-vacuum-analyze-before-entry-lock');
+injection_points_detach
+-----------------------
+
+(1 row)
+
diff --git a/src/test/modules/injection_points/meson.build b/src/test/modules/injection_points/meson.build
index 59dba1cb023..48cacfb81a8 100644
--- a/src/test/modules/injection_points/meson.build
+++ b/src/test/modules/injection_points/meson.build
@@ -51,6 +51,7 @@ tests += {
'repack_toast',
'syscache-update-pruned',
'heap_lock_update',
+ 'vacuum_skip_lock_stats',
],
'runningcheck': false, # see syscache-update-pruned
# Some tests wait for all snapshots, so avoid parallel execution
diff --git a/src/test/modules/injection_points/specs/vacuum_skip_lock_stats.spec b/src/test/modules/injection_points/specs/vacuum_skip_lock_stats.spec
new file mode 100644
index 00000000000..c7840bd915e
--- /dev/null
+++ b/src/test/modules/injection_points/specs/vacuum_skip_lock_stats.spec
@@ -0,0 +1,67 @@
+# Test for race conditions between VACUUM (SKIP_LOCKED) stats reporting
+# and concurrent DROP TABLE.
+#
+# When VACUUM (SKIP_LOCKED) cannot acquire a lock, it reports skipped
+# statistics via pgstat_report_skipped_vacuum_analyze(). An injection
+# point after the syscache lookup but before the stats update allows us
+# to verify that a concurrent DROP does not leave orphaned stats entries.
+
+setup
+{
+ CREATE EXTENSION injection_points;
+ CREATE TABLE test_skip (id int);
+ INSERT INTO test_skip VALUES (1);
+ ANALYZE test_skip;
+ SELECT pg_stat_force_next_flush();
+ CREATE TABLE saved_oid (oid_val oid);
+ INSERT INTO saved_oid SELECT oid FROM pg_class WHERE relname = 'test_skip';
+}
+
+teardown
+{
+ DROP TABLE IF EXISTS test_skip;
+ DROP TABLE IF EXISTS saved_oid;
+ DROP EXTENSION injection_points;
+}
+
+# s1: holds the lock so VACUUM skips the table
+session s1
+step lock
+{
+ BEGIN;
+ LOCK TABLE test_skip IN ACCESS EXCLUSIVE MODE;
+}
+step unlock { COMMIT; }
+
+# s2: runs VACUUM (SKIP_LOCKED), blocks at injection point after skip
+session s2
+setup
+{
+ SELECT injection_points_set_local();
+ SELECT injection_points_attach('skipped-vacuum-analyze-before-entry-lock', 'wait');
+}
+step vacuum { VACUUM (SKIP_LOCKED) test_skip; }
+step detach { SELECT injection_points_detach('skipped-vacuum-analyze-before-entry-lock'); }
+
+# s3: drops table or wakes up the vacuumer
+session s3
+step drop_table { DROP TABLE test_skip; }
+step rollback_drop
+{
+ BEGIN;
+ DROP TABLE test_skip;
+ ROLLBACK;
+}
+step wakeup { SELECT injection_points_wakeup('skipped-vacuum-analyze-before-entry-lock'); }
+step check_stats
+{
+ SELECT pg_stat_force_next_flush();
+ SELECT pg_stat_get_lock_skipped_vacuum_count(oid_val) AS skip_count
+ FROM saved_oid;
+}
+
+# Table dropped while vacuumer is blocked: no orphaned stats entry.
+permutation lock vacuum(wakeup) unlock drop_table wakeup check_stats detach
+
+# DROP rolled back while vacuumer is blocked: skip is still recorded.
+permutation lock vacuum(wakeup) unlock rollback_drop wakeup check_stats detach
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 05:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-15 09:02 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 16:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 20:21 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-16 14:29 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-11 08:23 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-21 18:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-21 18:51 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
@ 2026-07-22 04:20 ` Shinya Kato <shinya11.kato@gmail.com>
2026-07-24 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
0 siblings, 1 reply; 376+ messages in thread
From: Shinya Kato @ 2026-07-22 04:20 UTC (permalink / raw)
To: Sami Imseih <samimseih@gmail.com>; +Cc: Zsolt Parragi <zsolt.parragi@percona.com>; pgsql-hackers@lists.postgresql.org
On Wed, Jul 22, 2026 at 3:51 AM Sami Imseih <samimseih@gmail.com> wrote:
>
> Sorry, I missed attaching the injection point test.
>
> v11 includes it.
Thanks for the updated patches! I have a few comments.
In 0001,
> +step check_stat
> +{
> + SELECT relname,
> + vacuum_count, lock_skipped_vacuum_count,
> + analyze_count, lock_skipped_analyze_count
> + FROM pg_stat_all_tables
> + WHERE relname IN ('parted', 'part1', 'part2');
> +}
How about adding "ORDER BY relname" here? The row order is not
guaranteed otherwise.
> +{ oid => '8142', descr => 'statistics: last skipped vacuum time for a table',
> + proname => 'pg_stat_get_last_lock_skipped_vacuum_time', provolatile => 's',
The proname now says lock_skipped, but the descr still says just
"skipped". I think the descr should mention the lock cause as well,
and the same applies to the other seven entries.
> +#define PGSTAT_REPORT_SKIPPED_VACUUM (1 << 0) /* vacuum is skipped */
> +#define PGSTAT_REPORT_SKIPPED_ANALYZE (1 << 1) /* analyze is skipped */
The struct fields were renamed to lock_skipped_*, but these flags and
pgstat_report_skipped_vacuum_analyze() keep the generic name. Should
we rename them too, for consistency?
In 0002,
> + 'vacuum_skip_lock_stats',
This also needs to be added to the ISOLATION list in
src/test/modules/injection_points/Makefile. Without it the test never
runs in the autoconf build. I confirmed it passes once registered
there.
--
Best regards,
Shinya Kato
NTT OSS Center
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 05:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-15 09:02 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 16:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 20:21 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-16 14:29 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-11 08:23 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-21 18:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-21 18:51 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-22 04:20 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
@ 2026-07-24 16:48 ` Yugo Nagata <nagata@sraoss.co.jp>
2026-07-24 17:51 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-07-28 14:14 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
0 siblings, 2 replies; 376+ messages in thread
From: Yugo Nagata @ 2026-07-24 16:48 UTC (permalink / raw)
To: Shinya Kato <shinya11.kato@gmail.com>; +Cc: Sami Imseih <samimseih@gmail.com>; Zsolt Parragi <zsolt.parragi@percona.com>; pgsql-hackers@lists.postgresql.org
Hi,
I realized that I had been accidentally dropped from the CC list and had
missed the recent discussions. I'm sorry for my late response, and thank
you for keeping this moving.
I've attached the v12 patch, which incorporates Kato-san's feedback.
Regarding the column names, I noticed the change from skipped_* to lock_skipped_*.
While I initially preferred skipped_*, I understand Kato-san's point considering
that there could be other reasons for skipping in the future.
> In 0001,
>
> > +step check_stat
> > +{
> > + SELECT relname,
> > + vacuum_count, lock_skipped_vacuum_count,
> > + analyze_count, lock_skipped_analyze_count
> > + FROM pg_stat_all_tables
> > + WHERE relname IN ('parted', 'part1', 'part2');
> > +}
>
> How about adding "ORDER BY relname" here? The row order is not
> guaranteed otherwise.
Agreed. Fixed.
>
> > +{ oid => '8142', descr => 'statistics: last skipped vacuum time for a table',
> > + proname => 'pg_stat_get_last_lock_skipped_vacuum_time', provolatile => 's',
>
> The proname now says lock_skipped, but the descr still says just
> "skipped". I think the descr should mention the lock cause as well,
> and the same applies to the other seven entries.
I changed them like:
'statistics: last vacuum skip time due to lock unavailability for a table'
'statistics: number of vacuum skips due to lock unavailability for a table'
How do these sound?
> > +#define PGSTAT_REPORT_SKIPPED_VACUUM (1 << 0) /* vacuum is skipped */
> > +#define PGSTAT_REPORT_SKIPPED_ANALYZE (1 << 1) /* analyze is skipped */
>
> The struct fields were renamed to lock_skipped_*, but these flags and
> pgstat_report_skipped_vacuum_analyze() keep the generic name. Should
> we rename them too, for consistency?
I renamed them to PGSTAT_REPORT_LOCK_SKIPPED_*.
>
> In 0002,
>
> > + 'vacuum_skip_lock_stats',
>
> This also needs to be added to the ISOLATION list in
> src/test/modules/injection_points/Makefile. Without it the test never
> runs in the autoconf build. I confirmed it passes once registered
> there.
I fixed the Makefile.
Also, I added a comment explaining the RangeVarGetRelid() call with
NoLock in expand_vacuum_rel(), including why NoLock is used and why it is safe
in this case.
+ /*
+ * Get relid for statistics reporting.
+ *
+ * Since we failed to acquire the lock, use NoLock here. Although
+ * a concurrent DDL may have dropped or renamed the relation,
+ * RangeVarGetRelid() with NoLock does not check for invalidation
+ * messages.
+ */
+ relid = RangeVarGetRelid(vrel->relation, NoLock, true);
+
Regards,
Yugo Nagata
--
Yugo Nagata <nagata@sraoss.co.jp>
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 05:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-15 09:02 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 16:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 20:21 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-16 14:29 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-11 08:23 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-21 18:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-21 18:51 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-22 04:20 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-24 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
@ 2026-07-24 17:51 ` Yugo Nagata <nagata@sraoss.co.jp>
2026-07-27 04:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
1 sibling, 1 reply; 376+ messages in thread
From: Yugo Nagata @ 2026-07-24 17:51 UTC (permalink / raw)
To: Yugo Nagata <nagata@sraoss.co.jp>; +Cc: Shinya Kato <shinya11.kato@gmail.com>; Sami Imseih <samimseih@gmail.com>; Zsolt Parragi <zsolt.parragi@percona.com>; pgsql-hackers@lists.postgresql.org; Michael Paquier <michael@paquier.xyz>
Hi,
On Sat, 25 Jul 2026 01:48:32 +0900
Yugo Nagata <nagata@sraoss.co.jp> wrote:
> Hi,
>
> I realized that I had been accidentally dropped from the CC list and had
> missed the recent discussions. I'm sorry for my late response, and thank
> you for keeping this moving.
>
> I've attached the v12 patch, which incorporates Kato-san's feedback.
Sorry for the noise, but I'm resubmitting v12 to include the missing expected
files for the isolation tests.
I've also added Michael back to the CC list, as both he and I had accidentally
been dropped from the recent thread.
>
> Regarding the column names, I noticed the change from skipped_* to lock_skipped_*.
> While I initially preferred skipped_*, I understand Kato-san's point considering
> that there could be other reasons for skipping in the future.
>
> > In 0001,
> >
> > > +step check_stat
> > > +{
> > > + SELECT relname,
> > > + vacuum_count, lock_skipped_vacuum_count,
> > > + analyze_count, lock_skipped_analyze_count
> > > + FROM pg_stat_all_tables
> > > + WHERE relname IN ('parted', 'part1', 'part2');
> > > +}
> >
> > How about adding "ORDER BY relname" here? The row order is not
> > guaranteed otherwise.
>
> Agreed. Fixed.
>
> >
> > > +{ oid => '8142', descr => 'statistics: last skipped vacuum time for a table',
> > > + proname => 'pg_stat_get_last_lock_skipped_vacuum_time', provolatile => 's',
> >
> > The proname now says lock_skipped, but the descr still says just
> > "skipped". I think the descr should mention the lock cause as well,
> > and the same applies to the other seven entries.
>
> I changed them like:
>
> 'statistics: last vacuum skip time due to lock unavailability for a table'
> 'statistics: number of vacuum skips due to lock unavailability for a table'
>
> How do these sound?
>
> > > +#define PGSTAT_REPORT_SKIPPED_VACUUM (1 << 0) /* vacuum is skipped */
> > > +#define PGSTAT_REPORT_SKIPPED_ANALYZE (1 << 1) /* analyze is skipped */
> >
> > The struct fields were renamed to lock_skipped_*, but these flags and
> > pgstat_report_skipped_vacuum_analyze() keep the generic name. Should
> > we rename them too, for consistency?
>
> I renamed them to PGSTAT_REPORT_LOCK_SKIPPED_*.
>
> >
> > In 0002,
> >
> > > + 'vacuum_skip_lock_stats',
> >
> > This also needs to be added to the ISOLATION list in
> > src/test/modules/injection_points/Makefile. Without it the test never
> > runs in the autoconf build. I confirmed it passes once registered
> > there.
>
> I fixed the Makefile.
>
> Also, I added a comment explaining the RangeVarGetRelid() call with
> NoLock in expand_vacuum_rel(), including why NoLock is used and why it is safe
> in this case.
>
> + /*
> + * Get relid for statistics reporting.
> + *
> + * Since we failed to acquire the lock, use NoLock here. Although
> + * a concurrent DDL may have dropped or renamed the relation,
> + * RangeVarGetRelid() with NoLock does not check for invalidation
> + * messages.
> + */
> + relid = RangeVarGetRelid(vrel->relation, NoLock, true);
> +
Regards,
Yugo Nagata
--
Yugo Nagata <nagata@sraoss.co.jp>
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 05:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-15 09:02 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 16:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 20:21 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-16 14:29 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-11 08:23 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-21 18:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-21 18:51 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-22 04:20 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-24 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-07-24 17:51 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
@ 2026-07-27 04:03 ` Yugo Nagata <nagata@sraoss.co.jp>
2026-07-27 22:21 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
0 siblings, 1 reply; 376+ messages in thread
From: Yugo Nagata @ 2026-07-27 04:03 UTC (permalink / raw)
To: Sami Imseih <samimseih@gmail.com>; +Cc: Shinya Kato <shinya11.kato@gmail.com>; Zsolt Parragi <zsolt.parragi@percona.com>; pgsql-hackers@lists.postgresql.org; Michael Paquier <michael@paquier.xyz>
Hi Sami,
On Sat, 25 Jul 2026 02:51:59 +0900
Yugo Nagata <nagata@sraoss.co.jp> wrote:
> Hi,
>
> On Sat, 25 Jul 2026 01:48:32 +0900
> Yugo Nagata <nagata@sraoss.co.jp> wrote:
>
> > Hi,
> >
> > I realized that I had been accidentally dropped from the CC list and had
> > missed the recent discussions. I'm sorry for my late response, and thank
> > you for keeping this moving.
> >
> > I've attached the v12 patch, which incorporates Kato-san's feedback.
>
> Sorry for the noise, but I'm resubmitting v12 to include the missing expected
> files for the isolation tests.
>
> I've also added Michael back to the CC list, as both he and I had accidentally
> been dropped from the recent thread.
> >
> > Regarding the column names, I noticed the change from skipped_* to lock_skipped_*.
> > While I initially preferred skipped_*, I understand Kato-san's point considering
> > that there could be other reasons for skipping in the future.
> >
> > > In 0001,
> > >
> > > > +step check_stat
> > > > +{
> > > > + SELECT relname,
> > > > + vacuum_count, lock_skipped_vacuum_count,
> > > > + analyze_count, lock_skipped_analyze_count
> > > > + FROM pg_stat_all_tables
> > > > + WHERE relname IN ('parted', 'part1', 'part2');
> > > > +}
> > >
> > > How about adding "ORDER BY relname" here? The row order is not
> > > guaranteed otherwise.
> >
> > Agreed. Fixed.
> >
> > >
> > > > +{ oid => '8142', descr => 'statistics: last skipped vacuum time for a table',
> > > > + proname => 'pg_stat_get_last_lock_skipped_vacuum_time', provolatile => 's',
> > >
> > > The proname now says lock_skipped, but the descr still says just
> > > "skipped". I think the descr should mention the lock cause as well,
> > > and the same applies to the other seven entries.
> >
> > I changed them like:
> >
> > 'statistics: last vacuum skip time due to lock unavailability for a table'
> > 'statistics: number of vacuum skips due to lock unavailability for a table'
> >
> > How do these sound?
> >
> > > > +#define PGSTAT_REPORT_SKIPPED_VACUUM (1 << 0) /* vacuum is skipped */
> > > > +#define PGSTAT_REPORT_SKIPPED_ANALYZE (1 << 1) /* analyze is skipped */
> > >
> > > The struct fields were renamed to lock_skipped_*, but these flags and
> > > pgstat_report_skipped_vacuum_analyze() keep the generic name. Should
> > > we rename them too, for consistency?
> >
> > I renamed them to PGSTAT_REPORT_LOCK_SKIPPED_*.
> >
> > >
> > > In 0002,
> > >
> > > > + 'vacuum_skip_lock_stats',
> > >
> > > This also needs to be added to the ISOLATION list in
> > > src/test/modules/injection_points/Makefile. Without it the test never
> > > runs in the autoconf build. I confirmed it passes once registered
> > > there.
> >
> > I fixed the Makefile.
I found athat the injection_points's isolation test could fail [1]
because the check_stats step can be launched before the vacuum finishes.
I've attached a patch to fix this by inserting "noop" step in session s2
to ensure that the vacuum has finished.
Does this look good to you?
[1] https://github.com/postgresql-cfbot/postgresql/actions/runs/30115503531/attempts/1
> >
> > Also, I added a comment explaining the RangeVarGetRelid() call with
> > NoLock in expand_vacuum_rel(), including why NoLock is used and why it is safe
> > in this case.
> >
> > + /*
> > + * Get relid for statistics reporting.
> > + *
> > + * Since we failed to acquire the lock, use NoLock here. Although
> > + * a concurrent DDL may have dropped or renamed the relation,
> > + * RangeVarGetRelid() with NoLock does not check for invalidation
> > + * messages.
> > + */
> > + relid = RangeVarGetRelid(vrel->relation, NoLock, true);
> > +
Regards,
Yugo Nagata
--
Yugo Nagata <nagata@sraoss.co.jp>
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 05:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-15 09:02 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 16:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 20:21 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-16 14:29 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-11 08:23 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-21 18:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-21 18:51 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-22 04:20 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-24 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-07-24 17:51 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-07-27 04:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
@ 2026-07-27 22:21 ` Sami Imseih <samimseih@gmail.com>
0 siblings, 0 replies; 376+ messages in thread
From: Sami Imseih @ 2026-07-27 22:21 UTC (permalink / raw)
To: Yugo Nagata <nagata@sraoss.co.jp>; +Cc: Shinya Kato <shinya11.kato@gmail.com>; Zsolt Parragi <zsolt.parragi@percona.com>; pgsql-hackers@lists.postgresql.org; Michael Paquier <michael@paquier.xyz>
Hi,
> I've attached a patch to fix this by inserting "noop" step in session s2
> to ensure that the vacuum has finished.
>
> Does this look good to you?
>
> [1] https://github.com/postgresql-cfbot/postgresql/actions/runs/30115503531/attempts/1
Yes, this looks correct to me.
Thanks!
--
Sami Imseih
Amazon Web Services (AWS)
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 05:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-15 09:02 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 16:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 20:21 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-16 14:29 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-11 08:23 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-21 18:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-21 18:51 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-22 04:20 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-24 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
@ 2026-07-28 14:14 ` Shinya Kato <shinya11.kato@gmail.com>
2026-07-28 14:27 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
1 sibling, 1 reply; 376+ messages in thread
From: Shinya Kato @ 2026-07-28 14:14 UTC (permalink / raw)
To: Yugo Nagata <nagata@sraoss.co.jp>; +Cc: Sami Imseih <samimseih@gmail.com>; Zsolt Parragi <zsolt.parragi@percona.com>; pgsql-hackers@lists.postgresql.org; Michael Paquier <michael@paquier.xyz>
On Sat, Jul 25, 2026 at 1:48 AM Yugo Nagata <nagata@sraoss.co.jp> wrote:
> I've attached the v12 patch, which incorporates Kato-san's feedback.
>
> Regarding the column names, I noticed the change from skipped_* to lock_skipped_*.
> While I initially preferred skipped_*, I understand Kato-san's point considering
> that there could be other reasons for skipping in the future.
Thank you!
> > > +{ oid => '8142', descr => 'statistics: last skipped vacuum time for a table',
> > > + proname => 'pg_stat_get_last_lock_skipped_vacuum_time', provolatile => 's',
> >
> > The proname now says lock_skipped, but the descr still says just
> > "skipped". I think the descr should mention the lock cause as well,
> > and the same applies to the other seven entries.
>
> I changed them like:
>
> 'statistics: last vacuum skip time due to lock unavailability for a table'
> 'statistics: number of vacuum skips due to lock unavailability for a table'
>
> How do these sound?
These are well to me. One small thing about the analyze count entries:
> +{ oid => '8148', descr => 'statistics: number of analyzes skips due to lock unavailability for a table',
"analyzes skips" doubles the plural. Since "skips" is the noun here, I
think "analyze skips" is better, matching "vacuum skips" in 8146. The
same applies to 8149.
--
Best regards,
Shinya Kato
NTT OSS Center
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 05:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-15 09:02 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 16:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 20:21 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-16 14:29 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-11 08:23 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-21 18:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-21 18:51 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-22 04:20 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-24 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-07-28 14:14 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
@ 2026-07-28 14:27 ` Yugo Nagata <nagata@sraoss.co.jp>
2026-08-12 03:00 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
0 siblings, 1 reply; 376+ messages in thread
From: Yugo Nagata @ 2026-07-28 14:27 UTC (permalink / raw)
To: Shinya Kato <shinya11.kato@gmail.com>; +Cc: Sami Imseih <samimseih@gmail.com>; Zsolt Parragi <zsolt.parragi@percona.com>; pgsql-hackers@lists.postgresql.org; Michael Paquier <michael@paquier.xyz>
On Tue, 28 Jul 2026 23:14:32 +0900
Shinya Kato <shinya11.kato@gmail.com> wrote:
> On Sat, Jul 25, 2026 at 1:48 AM Yugo Nagata <nagata@sraoss.co.jp> wrote:
>
> > I've attached the v12 patch, which incorporates Kato-san's feedback.
> >
> > Regarding the column names, I noticed the change from skipped_* to lock_skipped_*.
> > While I initially preferred skipped_*, I understand Kato-san's point considering
> > that there could be other reasons for skipping in the future.
>
> Thank you!
>
> > > > +{ oid => '8142', descr => 'statistics: last skipped vacuum time for a table',
> > > > + proname => 'pg_stat_get_last_lock_skipped_vacuum_time', provolatile => 's',
> > >
> > > The proname now says lock_skipped, but the descr still says just
> > > "skipped". I think the descr should mention the lock cause as well,
> > > and the same applies to the other seven entries.
> >
> > I changed them like:
> >
> > 'statistics: last vacuum skip time due to lock unavailability for a table'
> > 'statistics: number of vacuum skips due to lock unavailability for a table'
> >
> > How do these sound?
>
> These are well to me. One small thing about the analyze count entries:
>
> > +{ oid => '8148', descr => 'statistics: number of analyzes skips due to lock unavailability for a table',
>
> "analyzes skips" doubles the plural. Since "skips" is the noun here, I
> think "analyze skips" is better, matching "vacuum skips" in 8146. The
> same applies to 8149.
Thanks for pointing it out.
I've updated the patch to fix this.
Regards,
Yugo Nagata
--
Yugo Nagata <nagata@sraoss.co.jp>
^ permalink raw reply [nested|flat] 376+ messages in thread
* Re: Track skipped tables during autovacuum and autoanalyze
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 05:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-15 09:02 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 16:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 20:21 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-16 14:29 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-11 08:23 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-21 18:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-21 18:51 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-22 04:20 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-24 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-07-28 14:14 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-28 14:27 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
@ 2026-08-12 03:00 ` Shinya Kato <shinya11.kato@gmail.com>
0 siblings, 0 replies; 376+ messages in thread
From: Shinya Kato @ 2026-08-12 03:00 UTC (permalink / raw)
To: Yugo Nagata <nagata@sraoss.co.jp>; +Cc: Sami Imseih <samimseih@gmail.com>; Zsolt Parragi <zsolt.parragi@percona.com>; pgsql-hackers@lists.postgresql.org; Michael Paquier <michael@paquier.xyz>
On Tue, Jul 28, 2026 at 11:27 PM Yugo Nagata <nagata@sraoss.co.jp> wrote:
> Thanks for pointing it out.
> I've updated the patch to fix this.
Thanks. v14 applies cleanly to master and check-world passes for me.
This time I looked at the reporting path itself and have a few
comments.
1. pgstat_report_skipped_vacuum_analyze() accesses the catalog while
holding the stats entry lock.
pgstat_get_entry_ref_locked() returns with the entry's LWLock held,
and the re-check runs under it. A catcache miss there opens pg_class,
so we can wait for a heavyweight lock while holding an LWLock, which
can deadlock. The deadlock detector does not see the LWLock side, and
LWLockAcquire() holds interrupts, so pg_cancel_backend() does not
break the wait either.
Could the re-check simply run after pgstat_unlock_entry()? The same
case then cancels immediately.
2. A skipped TOAST vacuum is also counted as a skipped analyze.
vacuum_rel() copies params for the TOAST recursion and only adds
VACOPT_PROCESS_MAIN, so VACOPT_ANALYZE survives into the recursive
call, and a contended lock on the toast table bumps its
lock_skipped_analyze_count as well. The comment just above that code
says analyze is never done on toast tables, so clearing the flag next
to the VACOPT_PROCESS_MAIN line looks right to me:
```
+ toast_vacuum_params.options &= ~VACOPT_ANALYZE;
```
3. Relations that can never be vacuumed get a stats entry.
The skip is reported before the relkind checks in vacuum_rel() and
analyze_rel(), so VACUUM (SKIP_LOCKED) on a locked view creates a
relation stats entry and bumps the counters. pg_stat_all_tables
filters on relkind, so nothing can ever display them. The function
already reads the pg_class tuple for relisshared, so it could pick up
relkind at the same time and return early for the relkinds those two
functions reject.
4. Smaller things:
- pgstat.h says "/* flags for pgstat_flush_backend() */" above the new
PGSTAT_REPORT_LOCK_SKIPPED_* macros. They are the flags of
pgstat_report_skipped_vacuum_analyze().
- The docs never mention SKIP_LOCKED. A manual command waits for the
lock otherwise, so the four manual columns only advance for commands
using that option. A sentence in the new note would cover it.
- last_lock_skipped_autoanalyze says "by the autovacuum" and "Last
time at which", while the three sibling entries say "by the autovacuum
daemon" and "Last time".
- The four non-auto descrs in pg_proc.dat drop "manual", although the
existing entries next to them say things like 'statistics: last manual
vacuum time for a table'.
Thoughts?
--
Shinya Kato
NTT OSS Center
^ permalink raw reply [nested|flat] 376+ messages in thread
end of thread, other threads:[~2026-08-12 03:00 UTC | newest]
Thread overview: 376+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2019-08-01 15:45 [PATCH 2/3] Force GIN recheck more accurately Nikita Glukhov <n.gluhov@postgrespro.ru>
2023-05-31 09:59 [PATCH v30 06/11] Add Incremental View Maintenance support Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 06:11 Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-24 14:58 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-24 16:28 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-25 02:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-25 04:24 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-25 17:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-25 23:26 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-26 01:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 01:31 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-26 02:25 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 10:22 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-26 23:07 ` Re: Track skipped tables during autovacuum and autoanalyze Michael Paquier <michael@paquier.xyz>
2026-03-27 07:35 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-03-27 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-03-28 07:18 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-13 08:05 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-04-22 12:49 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-04-27 11:32 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-04 20:44 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-12 09:47 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-12 14:50 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-14 21:46 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 00:59 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 05:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-05-15 09:02 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-15 16:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-05-15 20:21 ` Re: Track skipped tables during autovacuum and autoanalyze Zsolt Parragi <zsolt.parragi@percona.com>
2026-05-16 14:29 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-11 08:23 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-21 18:12 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-21 18:51 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-22 04:20 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-24 16:48 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-07-24 17:51 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-07-27 04:03 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-07-27 22:21 ` Re: Track skipped tables during autovacuum and autoanalyze Sami Imseih <samimseih@gmail.com>
2026-07-28 14:14 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
2026-07-28 14:27 ` Re: Track skipped tables during autovacuum and autoanalyze Yugo Nagata <nagata@sraoss.co.jp>
2026-08-12 03:00 ` Re: Track skipped tables during autovacuum and autoanalyze Shinya Kato <shinya11.kato@gmail.com>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox