public inbox for [email protected]  
help / color / mirror / Atom feed
From: Alena Rybakina <[email protected]>
To: Alexander Korotkov <[email protected]>
To: Andrey Borodin <[email protected]>
To: Andrei Lepikhov <[email protected]>
To: Andrei Zubkov <[email protected]>
Cc: pgsql-hackers <[email protected]>
Cc: Amit Kapila <[email protected]>
Cc: Jim Nasby <[email protected]>
Cc: Bertrand Drouvot <[email protected]>
Cc: Kirill Reshke <[email protected]>
Cc: Masahiko Sawada <[email protected]>
Cc: Melanie Plageman <[email protected]>
Cc: jian he <[email protected]>
Cc: Sami Imseih <[email protected]>
Cc: vignesh C <[email protected]>
Cc: Ilia Evdokimov <[email protected]>
Subject: Re: Vacuum statistics
Date: Mon, 30 Mar 2026 09:13:14 +0300
Message-ID: <[email protected]> (raw)
In-Reply-To: <[email protected]>
References: <[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>

Hi, all!

On 17.03.2026 21:11, Alena Rybakina wrote:

> I think last version is stable - it is in the isolation test. The last 
> version is here 
> https://www.postgresql.org/message-id/68939c47-fa0c-4198-853a-92d1390079da%40yandex.ru
>
Nothing special has been changed. I have rebased the patch because of 
updated PGSTAT_FILE_FORMAT_ID.

-----------
Best regards,
Alena Rybakina

From 7cea0dfa3c30805797a0a3d6ca8f8ac9b617d4a8 Mon Sep 17 00:00:00 2001
From: Alena Rybakina <[email protected]>
Date: Mon, 30 Mar 2026 09:07:24 +0300
Subject: [PATCH] Track table VM stability.

Add rev_all_visible_pages and rev_all_frozen_pages counters to
pg_stat_all_tables tracking the number of times the all-visible and
all-frozen bits are cleared in the visibility map. These bits are cleared by
backend processes during regular DML operations. Hence, the counters are placed
in table statistic entry.

A high rev_all_visible_pages rate relative to DML volume indicates
that modifications are scattered across previously-clean pages rather
than concentrated on already-dirty ones, causing index-only scans to
fall back to heap fetches.  A high rev_all_frozen_pages rate indicates
that vacuum's freezing work is being frequently undone by concurrent
DML.

Authors: Alena Rybakina <[email protected]>,
         Andrei Lepikhov <[email protected]>,
         Andrei Zubkov <[email protected]>
Reviewed-by: Dilip Kumar <[email protected]>,
         Masahiko Sawada <[email protected]>,
         Ilia Evdokimov <[email protected]>,
         Jian He <[email protected]>,
         Kirill Reshke <[email protected]>,
         Alexander Korotkov <[email protected]>,
         Jim Nasby <[email protected]>,
         Sami Imseih <[email protected]>,
         Karina Litskevich <[email protected]>,
	 Andrey Borodin <[email protected]>
---
 doc/src/sgml/monitoring.sgml                  |  32 +++
 src/backend/access/heap/visibilitymap.c       |  10 +
 src/backend/catalog/system_views.sql          |   4 +-
 src/backend/utils/activity/pgstat_relation.c  |   2 +
 src/backend/utils/adt/pgstatfuncs.c           |   6 +
 src/include/catalog/pg_proc.dat               |  10 +
 src/include/pgstat.h                          |  17 +-
 .../expected/vacuum-extending-freeze.out      | 185 ++++++++++++++++++
 src/test/isolation/isolation_schedule         |   1 +
 .../specs/vacuum-extending-freeze.spec        | 117 +++++++++++
 src/test/regress/expected/rules.out           |  12 +-
 11 files changed, 391 insertions(+), 5 deletions(-)
 create mode 100644 src/test/isolation/expected/vacuum-extending-freeze.out
 create mode 100644 src/test/isolation/specs/vacuum-extending-freeze.spec

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index bb75ed1069b..83c9e265624 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4362,6 +4362,38 @@ 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>visible_page_marks_cleared</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of times the all-visible mark in the
+       <link linkend="storage-vm">visibility map</link> was cleared for
+       pages of this table.  The all-visible mark of a heap page is
+       cleared whenever a backend process modifies a page that was
+       previously marked all-visible by vacuum activity (whether manual
+       <command>VACUUM</command> or autovacuum).  The page must then be
+       processed again by vacuum on a subsequent run.  A high rate of
+       change in this counter means that vacuum has to repeatedly
+       re-process pages of this table.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>frozen_page_marks_cleared</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of times the all-frozen mark in the
+       <link linkend="storage-vm">visibility map</link> was cleared for
+       pages of this table.  The all-frozen mark of a heap page is cleared
+       whenever a backend process modifies a page that was previously
+       marked all-frozen by vacuum activity (manual <command>VACUUM</command>
+       or autovacuum).  The page must then be processed again by vacuum on
+       the next freeze run for this table.
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>last_vacuum</structfield> <type>timestamp with time zone</type>
diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c
index 4fd470702aa..f055ec3819c 100644
--- a/src/backend/access/heap/visibilitymap.c
+++ b/src/backend/access/heap/visibilitymap.c
@@ -102,6 +102,7 @@
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "storage/bufmgr.h"
 #include "storage/smgr.h"
@@ -173,6 +174,15 @@ visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer vmbuf, uint8 flags
 
 	if (map[mapByte] & mask)
 	{
+		/*
+		 * Track how often all-visible or all-frozen bits are cleared in the
+		 * visibility map.
+		 */
+		if (map[mapByte] & ((flags & VISIBILITYMAP_ALL_VISIBLE) << mapOffset))
+			pgstat_count_visible_page_marks_cleared(rel);
+		if (map[mapByte] & ((flags & VISIBILITYMAP_ALL_FROZEN) << mapOffset))
+			pgstat_count_frozen_page_marks_cleared(rel);
+
 		map[mapByte] &= ~mask;
 
 		MarkBufferDirty(vmbuf);
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index e54018004db..855ea965583 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -747,7 +747,9 @@ CREATE VIEW pg_stat_all_tables AS
             pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
             pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
             pg_stat_get_total_autoanalyze_time(C.oid) AS total_autoanalyze_time,
-            pg_stat_get_stat_reset_time(C.oid) AS stats_reset
+            pg_stat_get_stat_reset_time(C.oid) AS stats_reset,
+            pg_stat_get_visible_page_marks_cleared(C.oid) AS visible_page_marks_cleared,
+            pg_stat_get_frozen_page_marks_cleared(C.oid) AS frozen_page_marks_cleared
     FROM pg_class C LEFT JOIN
          pg_index I ON C.oid = I.indrelid
          LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..78c0e6329bc 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -879,6 +879,8 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 
 	tabentry->blocks_fetched += lstats->counts.blocks_fetched;
 	tabentry->blocks_hit += lstats->counts.blocks_hit;
+	tabentry->visible_page_marks_cleared += lstats->counts.visible_page_marks_cleared;
+	tabentry->frozen_page_marks_cleared += lstats->counts.frozen_page_marks_cleared;
 
 	/* Clamp live_tuples in case of negative delta_live_tuples */
 	tabentry->live_tuples = Max(tabentry->live_tuples, 0);
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 9185a8e6b83..2e2b6897d36 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -108,6 +108,12 @@ PG_STAT_GET_RELENTRY_INT64(tuples_updated)
 /* pg_stat_get_vacuum_count */
 PG_STAT_GET_RELENTRY_INT64(vacuum_count)
 
+/* pg_stat_get_visible_page_marks_cleared */
+PG_STAT_GET_RELENTRY_INT64(visible_page_marks_cleared)
+
+/* pg_stat_get_frozen_page_marks_cleared */
+PG_STAT_GET_RELENTRY_INT64(frozen_page_marks_cleared)
+
 #define PG_STAT_GET_RELENTRY_FLOAT8(stat)						\
 Datum															\
 CppConcat(pg_stat_get_,stat)(PG_FUNCTION_ARGS)					\
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 0118e970dda..f6028006776 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12851,4 +12851,14 @@
   proname => 'hashoid8extended', prorettype => 'int8',
   proargtypes => 'oid8 int8', prosrc => 'hashoid8extended' },
 
+{ oid => '8002',
+  descr => 'statistics: number of times the all-visible marks in the visibility map were cleared for pages of this table',
+  proname => 'pg_stat_get_visible_page_marks_cleared', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_visible_page_marks_cleared' },
+{ oid => '8003',
+  descr => 'statistics: number of times the all-frozen marks in the visibility map were cleared for pages of this table',
+  proname => 'pg_stat_get_frozen_page_marks_cleared', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_frozen_page_marks_cleared' },
 ]
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..3a6d75892fa 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -160,6 +160,8 @@ typedef struct PgStat_TableCounts
 
 	PgStat_Counter blocks_fetched;
 	PgStat_Counter blocks_hit;
+	PgStat_Counter visible_page_marks_cleared;
+	PgStat_Counter frozen_page_marks_cleared;
 } PgStat_TableCounts;
 
 /* ----------
@@ -218,7 +220,7 @@ typedef struct PgStat_TableXactStatus
  * ------------------------------------------------------------
  */
 
-#define PGSTAT_FILE_FORMAT_ID	0x01A5BCBC
+#define PGSTAT_FILE_FORMAT_ID	0x01A5BCBD
 
 typedef struct PgStat_ArchiverStats
 {
@@ -469,6 +471,8 @@ typedef struct PgStat_StatTabEntry
 
 	PgStat_Counter blocks_fetched;
 	PgStat_Counter blocks_hit;
+	PgStat_Counter visible_page_marks_cleared;
+	PgStat_Counter frozen_page_marks_cleared;
 
 	TimestampTz last_vacuum_time;	/* user initiated vacuum */
 	PgStat_Counter vacuum_count;
@@ -753,6 +757,17 @@ extern void pgstat_report_analyze(Relation rel,
 		if (pgstat_should_count_relation(rel))						\
 			(rel)->pgstat_info->counts.blocks_hit++;				\
 	} while (0)
+/* count revocations of all-visible and all-frozen marks in visibility map */
+#define pgstat_count_visible_page_marks_cleared(rel)					\
+	do {															\
+		if (pgstat_should_count_relation(rel))						\
+			(rel)->pgstat_info->counts.visible_page_marks_cleared++;	\
+	} while (0)
+#define pgstat_count_frozen_page_marks_cleared(rel)					\
+	do {															\
+		if (pgstat_should_count_relation(rel))						\
+			(rel)->pgstat_info->counts.frozen_page_marks_cleared++;	\
+	} while (0)
 
 extern void pgstat_count_heap_insert(Relation rel, PgStat_Counter n);
 extern void pgstat_count_heap_update(Relation rel, bool hot, bool newpage);
diff --git a/src/test/isolation/expected/vacuum-extending-freeze.out b/src/test/isolation/expected/vacuum-extending-freeze.out
new file mode 100644
index 00000000000..994a8df56df
--- /dev/null
+++ b/src/test/isolation/expected/vacuum-extending-freeze.out
@@ -0,0 +1,185 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s2_vacuum_freeze s1_get_set_vm_flags_stats s1_update_table s1_get_cleared_vm_flags_stats s2_vacuum_freeze s1_get_set_vm_flags_stats s2_vacuum_freeze s1_select_from_index s2_delete_from_table s1_get_cleared_vm_flags_stats s2_vacuum_freeze s1_get_set_vm_flags_stats s1_commit s1_get_cleared_vm_flags_stats
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s2_vacuum_freeze: 
+    VACUUM FREEZE vestat;
+
+step s1_get_set_vm_flags_stats: 
+    SELECT pg_stat_force_next_flush();
+
+    SELECT c.relallfrozen > frozen_flag_count as relallfrozen, c.relallvisible > all_visibile_flag_count as relallvisible
+        FROM pg_class c, stats_state
+        WHERE c.relname = 'vestat';
+
+    UPDATE stats_state
+        SET frozen_flag_count = c.relallfrozen,
+            all_visibile_flag_count = c.relallvisible
+        FROM pg_class c
+        WHERE c.relname = 'vestat';
+
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+relallfrozen|relallvisible
+------------+-------------
+t           |t            
+(1 row)
+
+step s1_update_table: 
+    UPDATE vestat SET x = x + 1001 where x >= 2500;
+    SELECT pg_stat_force_next_flush();
+
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_get_cleared_vm_flags_stats: 
+    SELECT pg_stat_force_next_flush();
+
+    SELECT v.visible_page_marks_cleared > cleared_all_visibile_flag_count as visible_page_marks_cleared,
+           v.frozen_page_marks_cleared > cleared_frozen_flag_count as frozen_page_marks_cleared
+        FROM pg_stat_all_tables v, stats_state
+        WHERE v.relname = 'vestat';
+
+    UPDATE stats_state
+        SET cleared_all_visibile_flag_count = v.visible_page_marks_cleared,
+            cleared_frozen_flag_count = v.frozen_page_marks_cleared
+        FROM pg_stat_all_tables v
+        WHERE v.relname = 'vestat';
+
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+visible_page_marks_cleared|frozen_page_marks_cleared
+--------------------------+-------------------------
+t                         |t                        
+(1 row)
+
+step s2_vacuum_freeze: 
+    VACUUM FREEZE vestat;
+
+step s1_get_set_vm_flags_stats: 
+    SELECT pg_stat_force_next_flush();
+
+    SELECT c.relallfrozen > frozen_flag_count as relallfrozen, c.relallvisible > all_visibile_flag_count as relallvisible
+        FROM pg_class c, stats_state
+        WHERE c.relname = 'vestat';
+
+    UPDATE stats_state
+        SET frozen_flag_count = c.relallfrozen,
+            all_visibile_flag_count = c.relallvisible
+        FROM pg_class c
+        WHERE c.relname = 'vestat';
+
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+relallfrozen|relallvisible
+------------+-------------
+t           |t            
+(1 row)
+
+step s2_vacuum_freeze: 
+    VACUUM FREEZE vestat;
+
+step s1_select_from_index: 
+    BEGIN;
+    SELECT count(x) FROM vestat WHERE x > 2000;
+
+count
+-----
+ 3000
+(1 row)
+
+step s2_delete_from_table: 
+    DELETE FROM vestat WHERE x > 4930;
+
+step s1_get_cleared_vm_flags_stats: 
+    SELECT pg_stat_force_next_flush();
+
+    SELECT v.visible_page_marks_cleared > cleared_all_visibile_flag_count as visible_page_marks_cleared,
+           v.frozen_page_marks_cleared > cleared_frozen_flag_count as frozen_page_marks_cleared
+        FROM pg_stat_all_tables v, stats_state
+        WHERE v.relname = 'vestat';
+
+    UPDATE stats_state
+        SET cleared_all_visibile_flag_count = v.visible_page_marks_cleared,
+            cleared_frozen_flag_count = v.frozen_page_marks_cleared
+        FROM pg_stat_all_tables v
+        WHERE v.relname = 'vestat';
+
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+visible_page_marks_cleared|frozen_page_marks_cleared
+--------------------------+-------------------------
+f                         |f                        
+(1 row)
+
+step s2_vacuum_freeze: 
+    VACUUM FREEZE vestat;
+
+step s1_get_set_vm_flags_stats: 
+    SELECT pg_stat_force_next_flush();
+
+    SELECT c.relallfrozen > frozen_flag_count as relallfrozen, c.relallvisible > all_visibile_flag_count as relallvisible
+        FROM pg_class c, stats_state
+        WHERE c.relname = 'vestat';
+
+    UPDATE stats_state
+        SET frozen_flag_count = c.relallfrozen,
+            all_visibile_flag_count = c.relallvisible
+        FROM pg_class c
+        WHERE c.relname = 'vestat';
+
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+relallfrozen|relallvisible
+------------+-------------
+f           |f            
+(1 row)
+
+step s1_commit: 
+    COMMIT;
+
+step s1_get_cleared_vm_flags_stats: 
+    SELECT pg_stat_force_next_flush();
+
+    SELECT v.visible_page_marks_cleared > cleared_all_visibile_flag_count as visible_page_marks_cleared,
+           v.frozen_page_marks_cleared > cleared_frozen_flag_count as frozen_page_marks_cleared
+        FROM pg_stat_all_tables v, stats_state
+        WHERE v.relname = 'vestat';
+
+    UPDATE stats_state
+        SET cleared_all_visibile_flag_count = v.visible_page_marks_cleared,
+            cleared_frozen_flag_count = v.frozen_page_marks_cleared
+        FROM pg_stat_all_tables v
+        WHERE v.relname = 'vestat';
+
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+visible_page_marks_cleared|frozen_page_marks_cleared
+--------------------------+-------------------------
+t                         |t                        
+(1 row)
+
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 4e466580cd4..81e68f85d88 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -124,3 +124,4 @@ test: serializable-parallel-2
 test: serializable-parallel-3
 test: matview-write-skew
 test: lock-nowait
+test: vacuum-extending-freeze
diff --git a/src/test/isolation/specs/vacuum-extending-freeze.spec b/src/test/isolation/specs/vacuum-extending-freeze.spec
new file mode 100644
index 00000000000..17c204e2326
--- /dev/null
+++ b/src/test/isolation/specs/vacuum-extending-freeze.spec
@@ -0,0 +1,117 @@
+# In short, this test validates the correctness and stability of cumulative
+# vacuum statistics accounting around freezing, visibility, and revision
+# tracking across VACUUM and backend operations.
+# In addition, the test provides a scenario where one process holds a
+# transaction open while another process deletes tuples. We expect that
+# a backend clears the all-frozen and all-visible flags, which were set
+# by VACUUM earlier, only after the committing transaction makes the
+# deletions visible.
+
+setup
+{
+    CREATE TABLE vestat (x int, y int)
+        WITH (autovacuum_enabled = off, fillfactor = 70);
+
+    INSERT INTO vestat
+        SELECT i, i FROM generate_series(1, 5000) AS g(i);
+
+    CREATE INDEX vestat_idx ON vestat (x);
+
+    CREATE TABLE stats_state (frozen_flag_count int, all_visibile_flag_count int,
+                        cleared_frozen_flag_count int, cleared_all_visibile_flag_count int);
+    INSERT INTO stats_state VALUES (0,0,0,0);
+    ANALYZE vestat;
+
+    -- Ensure stats are flushed before starting the scenario
+    SELECT pg_stat_force_next_flush();
+}
+
+teardown
+{
+    DROP TABLE IF EXISTS vestat;
+    RESET vacuum_freeze_min_age;
+    RESET vacuum_freeze_table_age;
+
+}
+
+session s1
+
+step s1_get_set_vm_flags_stats
+{
+    SELECT pg_stat_force_next_flush();
+
+    SELECT c.relallfrozen > frozen_flag_count as relallfrozen, c.relallvisible > all_visibile_flag_count as relallvisible
+        FROM pg_class c, stats_state
+        WHERE c.relname = 'vestat';
+
+    UPDATE stats_state
+        SET frozen_flag_count = c.relallfrozen,
+            all_visibile_flag_count = c.relallvisible
+        FROM pg_class c
+        WHERE c.relname = 'vestat';
+}
+
+step s1_get_cleared_vm_flags_stats
+{
+    SELECT pg_stat_force_next_flush();
+
+    SELECT v.visible_page_marks_cleared > cleared_all_visibile_flag_count as visible_page_marks_cleared,
+           v.frozen_page_marks_cleared > cleared_frozen_flag_count as frozen_page_marks_cleared
+        FROM pg_stat_all_tables v, stats_state
+        WHERE v.relname = 'vestat';
+
+    UPDATE stats_state
+        SET cleared_all_visibile_flag_count = v.visible_page_marks_cleared,
+            cleared_frozen_flag_count = v.frozen_page_marks_cleared
+        FROM pg_stat_all_tables v
+        WHERE v.relname = 'vestat';
+}
+
+step s1_select_from_index
+{
+    BEGIN;
+    SELECT count(x) FROM vestat WHERE x > 2000;
+}
+
+step s1_commit
+{
+    COMMIT;
+}
+
+session s2
+setup
+{
+    -- Configure aggressive freezing vacuum behavior
+    SET vacuum_freeze_min_age = 0;
+    SET vacuum_freeze_table_age = 0;
+}
+step s2_delete_from_table
+{
+    DELETE FROM vestat WHERE x > 4930;
+}
+step s2_vacuum_freeze
+{
+    VACUUM FREEZE vestat;
+}
+
+step s1_update_table
+{
+    UPDATE vestat SET x = x + 1001 where x >= 2500;
+    SELECT pg_stat_force_next_flush();
+}
+
+permutation
+    s2_vacuum_freeze
+    s1_get_set_vm_flags_stats
+    s1_update_table
+    s1_get_cleared_vm_flags_stats
+    s2_vacuum_freeze
+    s1_get_set_vm_flags_stats
+    s2_vacuum_freeze
+    s1_select_from_index
+    s2_delete_from_table
+    s1_get_cleared_vm_flags_stats
+    s2_vacuum_freeze
+    s1_get_set_vm_flags_stats
+    s1_commit
+    s1_get_cleared_vm_flags_stats
\ No newline at end of file
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2b3cf6d8569..9036eb29988 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1846,7 +1846,9 @@ pg_stat_all_tables| SELECT c.oid AS relid,
     pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
     pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
     pg_stat_get_total_autoanalyze_time(c.oid) AS total_autoanalyze_time,
-    pg_stat_get_stat_reset_time(c.oid) AS stats_reset
+    pg_stat_get_stat_reset_time(c.oid) AS stats_reset,
+    pg_stat_get_visible_page_marks_cleared(c.oid) AS visible_page_marks_cleared,
+    pg_stat_get_frozen_page_marks_cleared(c.oid) AS frozen_page_marks_cleared
    FROM ((pg_class c
      LEFT JOIN pg_index i ON ((c.oid = i.indrelid)))
      LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
@@ -2304,7 +2306,9 @@ pg_stat_sys_tables| SELECT relid,
     total_autovacuum_time,
     total_analyze_time,
     total_autoanalyze_time,
-    stats_reset
+    stats_reset,
+    visible_page_marks_cleared,
+    frozen_page_marks_cleared
    FROM pg_stat_all_tables
   WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text));
 pg_stat_user_functions| SELECT p.oid AS funcid,
@@ -2359,7 +2363,9 @@ pg_stat_user_tables| SELECT relid,
     total_autovacuum_time,
     total_analyze_time,
     total_autoanalyze_time,
-    stats_reset
+    stats_reset,
+    visible_page_marks_cleared,
+    frozen_page_marks_cleared
    FROM pg_stat_all_tables
   WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text));
 pg_stat_wal| SELECT wal_records,
-- 
2.39.5 (Apple Git-154)



Attachments:

  [text/plain] v36-0001-Track-table-VM-stability.patch (21.7K, 2-v36-0001-Track-table-VM-stability.patch)
  download | inline diff:
From 7cea0dfa3c30805797a0a3d6ca8f8ac9b617d4a8 Mon Sep 17 00:00:00 2001
From: Alena Rybakina <[email protected]>
Date: Mon, 30 Mar 2026 09:07:24 +0300
Subject: [PATCH] Track table VM stability.

Add rev_all_visible_pages and rev_all_frozen_pages counters to
pg_stat_all_tables tracking the number of times the all-visible and
all-frozen bits are cleared in the visibility map. These bits are cleared by
backend processes during regular DML operations. Hence, the counters are placed
in table statistic entry.

A high rev_all_visible_pages rate relative to DML volume indicates
that modifications are scattered across previously-clean pages rather
than concentrated on already-dirty ones, causing index-only scans to
fall back to heap fetches.  A high rev_all_frozen_pages rate indicates
that vacuum's freezing work is being frequently undone by concurrent
DML.

Authors: Alena Rybakina <[email protected]>,
         Andrei Lepikhov <[email protected]>,
         Andrei Zubkov <[email protected]>
Reviewed-by: Dilip Kumar <[email protected]>,
         Masahiko Sawada <[email protected]>,
         Ilia Evdokimov <[email protected]>,
         Jian He <[email protected]>,
         Kirill Reshke <[email protected]>,
         Alexander Korotkov <[email protected]>,
         Jim Nasby <[email protected]>,
         Sami Imseih <[email protected]>,
         Karina Litskevich <[email protected]>,
	 Andrey Borodin <[email protected]>
---
 doc/src/sgml/monitoring.sgml                  |  32 +++
 src/backend/access/heap/visibilitymap.c       |  10 +
 src/backend/catalog/system_views.sql          |   4 +-
 src/backend/utils/activity/pgstat_relation.c  |   2 +
 src/backend/utils/adt/pgstatfuncs.c           |   6 +
 src/include/catalog/pg_proc.dat               |  10 +
 src/include/pgstat.h                          |  17 +-
 .../expected/vacuum-extending-freeze.out      | 185 ++++++++++++++++++
 src/test/isolation/isolation_schedule         |   1 +
 .../specs/vacuum-extending-freeze.spec        | 117 +++++++++++
 src/test/regress/expected/rules.out           |  12 +-
 11 files changed, 391 insertions(+), 5 deletions(-)
 create mode 100644 src/test/isolation/expected/vacuum-extending-freeze.out
 create mode 100644 src/test/isolation/specs/vacuum-extending-freeze.spec

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index bb75ed1069b..83c9e265624 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4362,6 +4362,38 @@ 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>visible_page_marks_cleared</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of times the all-visible mark in the
+       <link linkend="storage-vm">visibility map</link> was cleared for
+       pages of this table.  The all-visible mark of a heap page is
+       cleared whenever a backend process modifies a page that was
+       previously marked all-visible by vacuum activity (whether manual
+       <command>VACUUM</command> or autovacuum).  The page must then be
+       processed again by vacuum on a subsequent run.  A high rate of
+       change in this counter means that vacuum has to repeatedly
+       re-process pages of this table.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>frozen_page_marks_cleared</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of times the all-frozen mark in the
+       <link linkend="storage-vm">visibility map</link> was cleared for
+       pages of this table.  The all-frozen mark of a heap page is cleared
+       whenever a backend process modifies a page that was previously
+       marked all-frozen by vacuum activity (manual <command>VACUUM</command>
+       or autovacuum).  The page must then be processed again by vacuum on
+       the next freeze run for this table.
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>last_vacuum</structfield> <type>timestamp with time zone</type>
diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c
index 4fd470702aa..f055ec3819c 100644
--- a/src/backend/access/heap/visibilitymap.c
+++ b/src/backend/access/heap/visibilitymap.c
@@ -102,6 +102,7 @@
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "storage/bufmgr.h"
 #include "storage/smgr.h"
@@ -173,6 +174,15 @@ visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer vmbuf, uint8 flags
 
 	if (map[mapByte] & mask)
 	{
+		/*
+		 * Track how often all-visible or all-frozen bits are cleared in the
+		 * visibility map.
+		 */
+		if (map[mapByte] & ((flags & VISIBILITYMAP_ALL_VISIBLE) << mapOffset))
+			pgstat_count_visible_page_marks_cleared(rel);
+		if (map[mapByte] & ((flags & VISIBILITYMAP_ALL_FROZEN) << mapOffset))
+			pgstat_count_frozen_page_marks_cleared(rel);
+
 		map[mapByte] &= ~mask;
 
 		MarkBufferDirty(vmbuf);
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index e54018004db..855ea965583 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -747,7 +747,9 @@ CREATE VIEW pg_stat_all_tables AS
             pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
             pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
             pg_stat_get_total_autoanalyze_time(C.oid) AS total_autoanalyze_time,
-            pg_stat_get_stat_reset_time(C.oid) AS stats_reset
+            pg_stat_get_stat_reset_time(C.oid) AS stats_reset,
+            pg_stat_get_visible_page_marks_cleared(C.oid) AS visible_page_marks_cleared,
+            pg_stat_get_frozen_page_marks_cleared(C.oid) AS frozen_page_marks_cleared
     FROM pg_class C LEFT JOIN
          pg_index I ON C.oid = I.indrelid
          LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..78c0e6329bc 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -879,6 +879,8 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 
 	tabentry->blocks_fetched += lstats->counts.blocks_fetched;
 	tabentry->blocks_hit += lstats->counts.blocks_hit;
+	tabentry->visible_page_marks_cleared += lstats->counts.visible_page_marks_cleared;
+	tabentry->frozen_page_marks_cleared += lstats->counts.frozen_page_marks_cleared;
 
 	/* Clamp live_tuples in case of negative delta_live_tuples */
 	tabentry->live_tuples = Max(tabentry->live_tuples, 0);
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 9185a8e6b83..2e2b6897d36 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -108,6 +108,12 @@ PG_STAT_GET_RELENTRY_INT64(tuples_updated)
 /* pg_stat_get_vacuum_count */
 PG_STAT_GET_RELENTRY_INT64(vacuum_count)
 
+/* pg_stat_get_visible_page_marks_cleared */
+PG_STAT_GET_RELENTRY_INT64(visible_page_marks_cleared)
+
+/* pg_stat_get_frozen_page_marks_cleared */
+PG_STAT_GET_RELENTRY_INT64(frozen_page_marks_cleared)
+
 #define PG_STAT_GET_RELENTRY_FLOAT8(stat)						\
 Datum															\
 CppConcat(pg_stat_get_,stat)(PG_FUNCTION_ARGS)					\
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 0118e970dda..f6028006776 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12851,4 +12851,14 @@
   proname => 'hashoid8extended', prorettype => 'int8',
   proargtypes => 'oid8 int8', prosrc => 'hashoid8extended' },
 
+{ oid => '8002',
+  descr => 'statistics: number of times the all-visible marks in the visibility map were cleared for pages of this table',
+  proname => 'pg_stat_get_visible_page_marks_cleared', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_visible_page_marks_cleared' },
+{ oid => '8003',
+  descr => 'statistics: number of times the all-frozen marks in the visibility map were cleared for pages of this table',
+  proname => 'pg_stat_get_frozen_page_marks_cleared', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_frozen_page_marks_cleared' },
 ]
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8e3549c3752..3a6d75892fa 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -160,6 +160,8 @@ typedef struct PgStat_TableCounts
 
 	PgStat_Counter blocks_fetched;
 	PgStat_Counter blocks_hit;
+	PgStat_Counter visible_page_marks_cleared;
+	PgStat_Counter frozen_page_marks_cleared;
 } PgStat_TableCounts;
 
 /* ----------
@@ -218,7 +220,7 @@ typedef struct PgStat_TableXactStatus
  * ------------------------------------------------------------
  */
 
-#define PGSTAT_FILE_FORMAT_ID	0x01A5BCBC
+#define PGSTAT_FILE_FORMAT_ID	0x01A5BCBD
 
 typedef struct PgStat_ArchiverStats
 {
@@ -469,6 +471,8 @@ typedef struct PgStat_StatTabEntry
 
 	PgStat_Counter blocks_fetched;
 	PgStat_Counter blocks_hit;
+	PgStat_Counter visible_page_marks_cleared;
+	PgStat_Counter frozen_page_marks_cleared;
 
 	TimestampTz last_vacuum_time;	/* user initiated vacuum */
 	PgStat_Counter vacuum_count;
@@ -753,6 +757,17 @@ extern void pgstat_report_analyze(Relation rel,
 		if (pgstat_should_count_relation(rel))						\
 			(rel)->pgstat_info->counts.blocks_hit++;				\
 	} while (0)
+/* count revocations of all-visible and all-frozen marks in visibility map */
+#define pgstat_count_visible_page_marks_cleared(rel)					\
+	do {															\
+		if (pgstat_should_count_relation(rel))						\
+			(rel)->pgstat_info->counts.visible_page_marks_cleared++;	\
+	} while (0)
+#define pgstat_count_frozen_page_marks_cleared(rel)					\
+	do {															\
+		if (pgstat_should_count_relation(rel))						\
+			(rel)->pgstat_info->counts.frozen_page_marks_cleared++;	\
+	} while (0)
 
 extern void pgstat_count_heap_insert(Relation rel, PgStat_Counter n);
 extern void pgstat_count_heap_update(Relation rel, bool hot, bool newpage);
diff --git a/src/test/isolation/expected/vacuum-extending-freeze.out b/src/test/isolation/expected/vacuum-extending-freeze.out
new file mode 100644
index 00000000000..994a8df56df
--- /dev/null
+++ b/src/test/isolation/expected/vacuum-extending-freeze.out
@@ -0,0 +1,185 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s2_vacuum_freeze s1_get_set_vm_flags_stats s1_update_table s1_get_cleared_vm_flags_stats s2_vacuum_freeze s1_get_set_vm_flags_stats s2_vacuum_freeze s1_select_from_index s2_delete_from_table s1_get_cleared_vm_flags_stats s2_vacuum_freeze s1_get_set_vm_flags_stats s1_commit s1_get_cleared_vm_flags_stats
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s2_vacuum_freeze: 
+    VACUUM FREEZE vestat;
+
+step s1_get_set_vm_flags_stats: 
+    SELECT pg_stat_force_next_flush();
+
+    SELECT c.relallfrozen > frozen_flag_count as relallfrozen, c.relallvisible > all_visibile_flag_count as relallvisible
+        FROM pg_class c, stats_state
+        WHERE c.relname = 'vestat';
+
+    UPDATE stats_state
+        SET frozen_flag_count = c.relallfrozen,
+            all_visibile_flag_count = c.relallvisible
+        FROM pg_class c
+        WHERE c.relname = 'vestat';
+
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+relallfrozen|relallvisible
+------------+-------------
+t           |t            
+(1 row)
+
+step s1_update_table: 
+    UPDATE vestat SET x = x + 1001 where x >= 2500;
+    SELECT pg_stat_force_next_flush();
+
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_get_cleared_vm_flags_stats: 
+    SELECT pg_stat_force_next_flush();
+
+    SELECT v.visible_page_marks_cleared > cleared_all_visibile_flag_count as visible_page_marks_cleared,
+           v.frozen_page_marks_cleared > cleared_frozen_flag_count as frozen_page_marks_cleared
+        FROM pg_stat_all_tables v, stats_state
+        WHERE v.relname = 'vestat';
+
+    UPDATE stats_state
+        SET cleared_all_visibile_flag_count = v.visible_page_marks_cleared,
+            cleared_frozen_flag_count = v.frozen_page_marks_cleared
+        FROM pg_stat_all_tables v
+        WHERE v.relname = 'vestat';
+
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+visible_page_marks_cleared|frozen_page_marks_cleared
+--------------------------+-------------------------
+t                         |t                        
+(1 row)
+
+step s2_vacuum_freeze: 
+    VACUUM FREEZE vestat;
+
+step s1_get_set_vm_flags_stats: 
+    SELECT pg_stat_force_next_flush();
+
+    SELECT c.relallfrozen > frozen_flag_count as relallfrozen, c.relallvisible > all_visibile_flag_count as relallvisible
+        FROM pg_class c, stats_state
+        WHERE c.relname = 'vestat';
+
+    UPDATE stats_state
+        SET frozen_flag_count = c.relallfrozen,
+            all_visibile_flag_count = c.relallvisible
+        FROM pg_class c
+        WHERE c.relname = 'vestat';
+
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+relallfrozen|relallvisible
+------------+-------------
+t           |t            
+(1 row)
+
+step s2_vacuum_freeze: 
+    VACUUM FREEZE vestat;
+
+step s1_select_from_index: 
+    BEGIN;
+    SELECT count(x) FROM vestat WHERE x > 2000;
+
+count
+-----
+ 3000
+(1 row)
+
+step s2_delete_from_table: 
+    DELETE FROM vestat WHERE x > 4930;
+
+step s1_get_cleared_vm_flags_stats: 
+    SELECT pg_stat_force_next_flush();
+
+    SELECT v.visible_page_marks_cleared > cleared_all_visibile_flag_count as visible_page_marks_cleared,
+           v.frozen_page_marks_cleared > cleared_frozen_flag_count as frozen_page_marks_cleared
+        FROM pg_stat_all_tables v, stats_state
+        WHERE v.relname = 'vestat';
+
+    UPDATE stats_state
+        SET cleared_all_visibile_flag_count = v.visible_page_marks_cleared,
+            cleared_frozen_flag_count = v.frozen_page_marks_cleared
+        FROM pg_stat_all_tables v
+        WHERE v.relname = 'vestat';
+
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+visible_page_marks_cleared|frozen_page_marks_cleared
+--------------------------+-------------------------
+f                         |f                        
+(1 row)
+
+step s2_vacuum_freeze: 
+    VACUUM FREEZE vestat;
+
+step s1_get_set_vm_flags_stats: 
+    SELECT pg_stat_force_next_flush();
+
+    SELECT c.relallfrozen > frozen_flag_count as relallfrozen, c.relallvisible > all_visibile_flag_count as relallvisible
+        FROM pg_class c, stats_state
+        WHERE c.relname = 'vestat';
+
+    UPDATE stats_state
+        SET frozen_flag_count = c.relallfrozen,
+            all_visibile_flag_count = c.relallvisible
+        FROM pg_class c
+        WHERE c.relname = 'vestat';
+
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+relallfrozen|relallvisible
+------------+-------------
+f           |f            
+(1 row)
+
+step s1_commit: 
+    COMMIT;
+
+step s1_get_cleared_vm_flags_stats: 
+    SELECT pg_stat_force_next_flush();
+
+    SELECT v.visible_page_marks_cleared > cleared_all_visibile_flag_count as visible_page_marks_cleared,
+           v.frozen_page_marks_cleared > cleared_frozen_flag_count as frozen_page_marks_cleared
+        FROM pg_stat_all_tables v, stats_state
+        WHERE v.relname = 'vestat';
+
+    UPDATE stats_state
+        SET cleared_all_visibile_flag_count = v.visible_page_marks_cleared,
+            cleared_frozen_flag_count = v.frozen_page_marks_cleared
+        FROM pg_stat_all_tables v
+        WHERE v.relname = 'vestat';
+
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+visible_page_marks_cleared|frozen_page_marks_cleared
+--------------------------+-------------------------
+t                         |t                        
+(1 row)
+
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 4e466580cd4..81e68f85d88 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -124,3 +124,4 @@ test: serializable-parallel-2
 test: serializable-parallel-3
 test: matview-write-skew
 test: lock-nowait
+test: vacuum-extending-freeze
diff --git a/src/test/isolation/specs/vacuum-extending-freeze.spec b/src/test/isolation/specs/vacuum-extending-freeze.spec
new file mode 100644
index 00000000000..17c204e2326
--- /dev/null
+++ b/src/test/isolation/specs/vacuum-extending-freeze.spec
@@ -0,0 +1,117 @@
+# In short, this test validates the correctness and stability of cumulative
+# vacuum statistics accounting around freezing, visibility, and revision
+# tracking across VACUUM and backend operations.
+# In addition, the test provides a scenario where one process holds a
+# transaction open while another process deletes tuples. We expect that
+# a backend clears the all-frozen and all-visible flags, which were set
+# by VACUUM earlier, only after the committing transaction makes the
+# deletions visible.
+
+setup
+{
+    CREATE TABLE vestat (x int, y int)
+        WITH (autovacuum_enabled = off, fillfactor = 70);
+
+    INSERT INTO vestat
+        SELECT i, i FROM generate_series(1, 5000) AS g(i);
+
+    CREATE INDEX vestat_idx ON vestat (x);
+
+    CREATE TABLE stats_state (frozen_flag_count int, all_visibile_flag_count int,
+                        cleared_frozen_flag_count int, cleared_all_visibile_flag_count int);
+    INSERT INTO stats_state VALUES (0,0,0,0);
+    ANALYZE vestat;
+
+    -- Ensure stats are flushed before starting the scenario
+    SELECT pg_stat_force_next_flush();
+}
+
+teardown
+{
+    DROP TABLE IF EXISTS vestat;
+    RESET vacuum_freeze_min_age;
+    RESET vacuum_freeze_table_age;
+
+}
+
+session s1
+
+step s1_get_set_vm_flags_stats
+{
+    SELECT pg_stat_force_next_flush();
+
+    SELECT c.relallfrozen > frozen_flag_count as relallfrozen, c.relallvisible > all_visibile_flag_count as relallvisible
+        FROM pg_class c, stats_state
+        WHERE c.relname = 'vestat';
+
+    UPDATE stats_state
+        SET frozen_flag_count = c.relallfrozen,
+            all_visibile_flag_count = c.relallvisible
+        FROM pg_class c
+        WHERE c.relname = 'vestat';
+}
+
+step s1_get_cleared_vm_flags_stats
+{
+    SELECT pg_stat_force_next_flush();
+
+    SELECT v.visible_page_marks_cleared > cleared_all_visibile_flag_count as visible_page_marks_cleared,
+           v.frozen_page_marks_cleared > cleared_frozen_flag_count as frozen_page_marks_cleared
+        FROM pg_stat_all_tables v, stats_state
+        WHERE v.relname = 'vestat';
+
+    UPDATE stats_state
+        SET cleared_all_visibile_flag_count = v.visible_page_marks_cleared,
+            cleared_frozen_flag_count = v.frozen_page_marks_cleared
+        FROM pg_stat_all_tables v
+        WHERE v.relname = 'vestat';
+}
+
+step s1_select_from_index
+{
+    BEGIN;
+    SELECT count(x) FROM vestat WHERE x > 2000;
+}
+
+step s1_commit
+{
+    COMMIT;
+}
+
+session s2
+setup
+{
+    -- Configure aggressive freezing vacuum behavior
+    SET vacuum_freeze_min_age = 0;
+    SET vacuum_freeze_table_age = 0;
+}
+step s2_delete_from_table
+{
+    DELETE FROM vestat WHERE x > 4930;
+}
+step s2_vacuum_freeze
+{
+    VACUUM FREEZE vestat;
+}
+
+step s1_update_table
+{
+    UPDATE vestat SET x = x + 1001 where x >= 2500;
+    SELECT pg_stat_force_next_flush();
+}
+
+permutation
+    s2_vacuum_freeze
+    s1_get_set_vm_flags_stats
+    s1_update_table
+    s1_get_cleared_vm_flags_stats
+    s2_vacuum_freeze
+    s1_get_set_vm_flags_stats
+    s2_vacuum_freeze
+    s1_select_from_index
+    s2_delete_from_table
+    s1_get_cleared_vm_flags_stats
+    s2_vacuum_freeze
+    s1_get_set_vm_flags_stats
+    s1_commit
+    s1_get_cleared_vm_flags_stats
\ No newline at end of file
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2b3cf6d8569..9036eb29988 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1846,7 +1846,9 @@ pg_stat_all_tables| SELECT c.oid AS relid,
     pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
     pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
     pg_stat_get_total_autoanalyze_time(c.oid) AS total_autoanalyze_time,
-    pg_stat_get_stat_reset_time(c.oid) AS stats_reset
+    pg_stat_get_stat_reset_time(c.oid) AS stats_reset,
+    pg_stat_get_visible_page_marks_cleared(c.oid) AS visible_page_marks_cleared,
+    pg_stat_get_frozen_page_marks_cleared(c.oid) AS frozen_page_marks_cleared
    FROM ((pg_class c
      LEFT JOIN pg_index i ON ((c.oid = i.indrelid)))
      LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
@@ -2304,7 +2306,9 @@ pg_stat_sys_tables| SELECT relid,
     total_autovacuum_time,
     total_analyze_time,
     total_autoanalyze_time,
-    stats_reset
+    stats_reset,
+    visible_page_marks_cleared,
+    frozen_page_marks_cleared
    FROM pg_stat_all_tables
   WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text));
 pg_stat_user_functions| SELECT p.oid AS funcid,
@@ -2359,7 +2363,9 @@ pg_stat_user_tables| SELECT relid,
     total_autovacuum_time,
     total_analyze_time,
     total_autoanalyze_time,
-    stats_reset
+    stats_reset,
+    visible_page_marks_cleared,
+    frozen_page_marks_cleared
    FROM pg_stat_all_tables
   WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text));
 pg_stat_wal| SELECT wal_records,
-- 
2.39.5 (Apple Git-154)



view thread (75+ messages)  latest in thread

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
  Subject: Re: Vacuum statistics
  In-Reply-To: <[email protected]>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

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