You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudberry.apache.org by dj...@apache.org on 2026/10/11 01:41:48 UTC

(cloudberry) 01/07: Fix flaky autovacuum-analyze tests

This is an automated email from the ASF dual-hosted git repository.

tuhaihe pushed a commit to branch REL_2_STABLE
in repository https://gitbox.apache.org/repos/asf/cloudberry.git

commit 696cb3ad4802cd192170a04f80863e2a315eba9d
Author: Dianjin Wang <wa...@gmail.com>
AuthorDate: Tue Sep 1 21:23:23 2026 +0800

    Fix flaky autovacuum-analyze tests
    
    REL_2_STABLE has no SQL function to force pending table statistics to
    flush. Close each writer session to flush on backend exit, then poll
    the collector counters with a fresh statistics snapshot on every retry.
    Restore on_change mode when reconnecting for the third scenario.
    
    Wait for analyze, autoanalyze and modification counts before checking
    the results, and raise an error if the expected state is not reached.
    Apply the helper and expected results to regular, PAX and single-node
    tests.
    
    Assisted-by: Claude Code
    Assisted-by: ChatGPT
    Backpatch-through: REL_2_STABLE
---
 .../isolation2/input/autovacuum-analyze.source     | 38 ++++++++++++++--
 .../isolation2/output/autovacuum-analyze.source    | 51 ++++++++++++++++++++--
 .../isolation2/input/autovacuum-analyze.source     | 38 ++++++++++++++--
 .../isolation2/output/autovacuum-analyze.source    | 51 ++++++++++++++++++++--
 .../input/autovacuum-analyze.source                | 38 ++++++++++++++--
 .../output/autovacuum-analyze.source               | 51 ++++++++++++++++++++--
 6 files changed, 249 insertions(+), 18 deletions(-)

diff --git a/contrib/pax_storage/src/test/isolation2/input/autovacuum-analyze.source b/contrib/pax_storage/src/test/isolation2/input/autovacuum-analyze.source
index 32e79cdd491..035325d9f49 100644
--- a/contrib/pax_storage/src/test/isolation2/input/autovacuum-analyze.source
+++ b/contrib/pax_storage/src/test/isolation2/input/autovacuum-analyze.source
@@ -158,6 +158,26 @@ select relpages, reltuples from pg_class where oid = 'anaabort'::regclass;
 -- src/backend/postmaster/README.auto-ANALYZE.
 --
 
+-- Refresh the collector snapshot until the expected counters are visible.
+CREATE FUNCTION wait_for_analyze_stats(rel regclass, expected_analyze bigint,
+                                      expected_autoanalyze bigint, expected_mod bigint)
+RETURNS bool AS $$
+DECLARE
+    retries int; /* in func */
+BEGIN
+    FOR retries IN 1..600 LOOP
+        PERFORM pg_stat_clear_snapshot(); /* in func */
+        IF pg_stat_get_analyze_count(rel) = expected_analyze
+           AND pg_stat_get_autoanalyze_count(rel) = expected_autoanalyze
+           AND pg_stat_get_mod_since_analyze(rel) = expected_mod THEN
+            RETURN true; /* in func */
+        END IF; /* in func */
+        PERFORM pg_sleep(0.1); /* in func */
+    END LOOP; /* in func */
+    RAISE EXCEPTION 'timed out waiting for analyze statistics for %', rel; /* in func */
+END; /* in func */
+$$ LANGUAGE plpgsql;
+
 -- Case 1 --
 2: SET gp_autostats_mode = 'on_no_stats';
 
@@ -171,7 +191,9 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '
 
 -- with auto_stats, the auto-ANALYZE still trigger
 2: INSERT INTO autostatstbl select i from generate_series(1, 1000) as i;
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
+-- Disconnect the writer to flush pending statistics to the collector.
+2q:
+SELECT wait_for_analyze_stats('autostatstbl', 1, 0, 1000);
 
 -- auto_stats executed but auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 SELECT count(*) FROM pg_statistic where starelid = 'autostatstbl'::regclass;
@@ -189,6 +211,7 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'reset', 1);
 SELECT gp_wait_until_triggered_fault('analyze_finished_one_relation', 1, 1);
 SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
 
+SELECT wait_for_analyze_stats('autostatstbl', 1, 1, 0);
 -- we can see the auto-ANALYZE finished
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
 -- expect analyze_count = 1, autoanalyze_count = 1, and n_mod_since_analyze = 0
@@ -205,7 +228,9 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 'skip', '', '', 'autosta
 SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '', 'autostatstbl', 1, -1, 0, 1);
 
 2: INSERT INTO autostatstbl select i from generate_series(1001, 2000) as i;
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
+-- Disconnect the writer to flush pending statistics to the collector.
+2q:
+SELECT wait_for_analyze_stats('autostatstbl', 2, 1, 1000);
 
 -- auto_stats executed but auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
@@ -222,6 +247,7 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'reset', 1);
 SELECT gp_wait_until_triggered_fault('analyze_finished_one_relation', 1, 1);
 SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
 
+SELECT wait_for_analyze_stats('autostatstbl', 2, 2, 0);
 -- we can see the auto-ANALYZE finished, check statistic and analyze count
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
 -- expect analyze_count = 2, autoanalyze_count = 2, and n_mod_since_analyze = 0
@@ -230,6 +256,7 @@ select analyze_count, autoanalyze_count, n_mod_since_analyze from pg_stat_all_ta
 
 -- Case 3 --
 -- reset the threshold, so auto_stats will not be triggered and then auto-ANALYZE will be triggered.
+2: SET gp_autostats_mode = 'on_change';
 2: RESET gp_autostats_on_change_threshold;
 
 -- Track that we have updated the attributes stats in pg_statistic when finished
@@ -238,7 +265,9 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 'skip', '', '', 'autosta
 SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '', 'autostatstbl', 1, -1, 0, 1);
 
 2: INSERT INTO autostatstbl select i from generate_series(2001, 3000) as i;
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
+-- Disconnect the writer to flush pending statistics to the collector.
+2q:
+SELECT wait_for_analyze_stats('autostatstbl', 2, 2, 1000);
 
 -- auto_stats should not executed and auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
@@ -255,11 +284,14 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'reset', 1);
 SELECT gp_wait_until_triggered_fault('analyze_finished_one_relation', 1, 1);
 SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
 
+SELECT wait_for_analyze_stats('autostatstbl', 2, 3, 0);
 -- we can see the auto-ANALYZE finished, check statistic and analyze count
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
 -- expect analyze_count = 2, autoanalyze_count = 3, and n_mod_since_analyze = 0 since ANALYZE executed
 select analyze_count, autoanalyze_count, n_mod_since_analyze from pg_stat_all_tables_internal where relname = 'autostatstbl';
 
+DROP FUNCTION wait_for_analyze_stats(regclass, bigint, bigint, bigint);
+
 -- Reset GUCs.
 ALTER SYSTEM RESET autovacuum_naptime;
 select * from pg_reload_conf();
diff --git a/contrib/pax_storage/src/test/isolation2/output/autovacuum-analyze.source b/contrib/pax_storage/src/test/isolation2/output/autovacuum-analyze.source
index 0c4b87746e4..88c1cabb96a 100644
--- a/contrib/pax_storage/src/test/isolation2/output/autovacuum-analyze.source
+++ b/contrib/pax_storage/src/test/isolation2/output/autovacuum-analyze.source
@@ -384,6 +384,10 @@ END
 -- src/backend/postmaster/README.auto-ANALYZE.
 --
 
+-- Refresh the collector snapshot until the expected counters are visible.
+CREATE FUNCTION wait_for_analyze_stats(rel regclass, expected_analyze bigint, expected_autoanalyze bigint, expected_mod bigint) RETURNS bool AS $$ DECLARE retries int; /* in func */ BEGIN FOR retries IN 1..600 LOOP PERFORM pg_stat_clear_snapshot(); /* in func */ IF pg_stat_get_analyze_count(rel) = expected_analyze AND pg_stat_get_autoanalyze_count(rel) = expected_autoanalyze AND pg_stat_get_mod_since_analyze(rel) = expected_mod THEN RETURN true; /* in func */ END IF; /* in func */ PERFOR [...]
+CREATE
+
 -- Case 1 --
 2: SET gp_autostats_mode = 'on_no_stats';
 SET
@@ -408,7 +412,14 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '
 -- with auto_stats, the auto-ANALYZE still trigger
 2: INSERT INTO autostatstbl select i from generate_series(1, 1000) as i;
 INSERT 1000
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. 
+-- Disconnect the writer to flush pending statistics to the collector.
+2q: ... <quitting>
+SELECT wait_for_analyze_stats('autostatstbl', 1, 0, 1000);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
+
 -- auto_stats executed but auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 SELECT count(*) FROM pg_statistic where starelid = 'autostatstbl'::regclass;
  count 
@@ -458,6 +469,11 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
  Success:        
 (1 row)
 
+SELECT wait_for_analyze_stats('autostatstbl', 1, 1, 0);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
 -- we can see the auto-ANALYZE finished
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
  relpages | reltuples 
@@ -493,7 +509,14 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '
 
 2: INSERT INTO autostatstbl select i from generate_series(1001, 2000) as i;
 INSERT 1000
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. 
+-- Disconnect the writer to flush pending statistics to the collector.
+2q: ... <quitting>
+SELECT wait_for_analyze_stats('autostatstbl', 2, 1, 1000);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
+
 -- auto_stats executed but auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
  relpages | reltuples 
@@ -538,6 +561,11 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
  Success:        
 (1 row)
 
+SELECT wait_for_analyze_stats('autostatstbl', 2, 2, 0);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
 -- we can see the auto-ANALYZE finished, check statistic and analyze count
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
  relpages | reltuples 
@@ -554,6 +582,8 @@ select analyze_count, autoanalyze_count, n_mod_since_analyze from pg_stat_all_ta
 
 -- Case 3 --
 -- reset the threshold, so auto_stats will not be triggered and then auto-ANALYZE will be triggered.
+2: SET gp_autostats_mode = 'on_change';
+SET
 2: RESET gp_autostats_on_change_threshold;
 RESET
 
@@ -572,7 +602,14 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '
 
 2: INSERT INTO autostatstbl select i from generate_series(2001, 3000) as i;
 INSERT 1000
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. 
+-- Disconnect the writer to flush pending statistics to the collector.
+2q: ... <quitting>
+SELECT wait_for_analyze_stats('autostatstbl', 2, 2, 1000);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
+
 -- auto_stats should not executed and auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
  relpages | reltuples 
@@ -617,6 +654,11 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
  Success:        
 (1 row)
 
+SELECT wait_for_analyze_stats('autostatstbl', 2, 3, 0);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
 -- we can see the auto-ANALYZE finished, check statistic and analyze count
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
  relpages | reltuples 
@@ -630,6 +672,9 @@ select analyze_count, autoanalyze_count, n_mod_since_analyze from pg_stat_all_ta
  2             | 3                 | 0                   
 (1 row)
 
+DROP FUNCTION wait_for_analyze_stats(regclass, bigint, bigint, bigint);
+DROP
+
 -- Reset GUCs.
 ALTER SYSTEM RESET autovacuum_naptime;
 ALTER
diff --git a/src/test/isolation2/input/autovacuum-analyze.source b/src/test/isolation2/input/autovacuum-analyze.source
index 32e79cdd491..035325d9f49 100644
--- a/src/test/isolation2/input/autovacuum-analyze.source
+++ b/src/test/isolation2/input/autovacuum-analyze.source
@@ -158,6 +158,26 @@ select relpages, reltuples from pg_class where oid = 'anaabort'::regclass;
 -- src/backend/postmaster/README.auto-ANALYZE.
 --
 
+-- Refresh the collector snapshot until the expected counters are visible.
+CREATE FUNCTION wait_for_analyze_stats(rel regclass, expected_analyze bigint,
+                                      expected_autoanalyze bigint, expected_mod bigint)
+RETURNS bool AS $$
+DECLARE
+    retries int; /* in func */
+BEGIN
+    FOR retries IN 1..600 LOOP
+        PERFORM pg_stat_clear_snapshot(); /* in func */
+        IF pg_stat_get_analyze_count(rel) = expected_analyze
+           AND pg_stat_get_autoanalyze_count(rel) = expected_autoanalyze
+           AND pg_stat_get_mod_since_analyze(rel) = expected_mod THEN
+            RETURN true; /* in func */
+        END IF; /* in func */
+        PERFORM pg_sleep(0.1); /* in func */
+    END LOOP; /* in func */
+    RAISE EXCEPTION 'timed out waiting for analyze statistics for %', rel; /* in func */
+END; /* in func */
+$$ LANGUAGE plpgsql;
+
 -- Case 1 --
 2: SET gp_autostats_mode = 'on_no_stats';
 
@@ -171,7 +191,9 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '
 
 -- with auto_stats, the auto-ANALYZE still trigger
 2: INSERT INTO autostatstbl select i from generate_series(1, 1000) as i;
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
+-- Disconnect the writer to flush pending statistics to the collector.
+2q:
+SELECT wait_for_analyze_stats('autostatstbl', 1, 0, 1000);
 
 -- auto_stats executed but auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 SELECT count(*) FROM pg_statistic where starelid = 'autostatstbl'::regclass;
@@ -189,6 +211,7 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'reset', 1);
 SELECT gp_wait_until_triggered_fault('analyze_finished_one_relation', 1, 1);
 SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
 
+SELECT wait_for_analyze_stats('autostatstbl', 1, 1, 0);
 -- we can see the auto-ANALYZE finished
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
 -- expect analyze_count = 1, autoanalyze_count = 1, and n_mod_since_analyze = 0
@@ -205,7 +228,9 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 'skip', '', '', 'autosta
 SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '', 'autostatstbl', 1, -1, 0, 1);
 
 2: INSERT INTO autostatstbl select i from generate_series(1001, 2000) as i;
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
+-- Disconnect the writer to flush pending statistics to the collector.
+2q:
+SELECT wait_for_analyze_stats('autostatstbl', 2, 1, 1000);
 
 -- auto_stats executed but auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
@@ -222,6 +247,7 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'reset', 1);
 SELECT gp_wait_until_triggered_fault('analyze_finished_one_relation', 1, 1);
 SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
 
+SELECT wait_for_analyze_stats('autostatstbl', 2, 2, 0);
 -- we can see the auto-ANALYZE finished, check statistic and analyze count
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
 -- expect analyze_count = 2, autoanalyze_count = 2, and n_mod_since_analyze = 0
@@ -230,6 +256,7 @@ select analyze_count, autoanalyze_count, n_mod_since_analyze from pg_stat_all_ta
 
 -- Case 3 --
 -- reset the threshold, so auto_stats will not be triggered and then auto-ANALYZE will be triggered.
+2: SET gp_autostats_mode = 'on_change';
 2: RESET gp_autostats_on_change_threshold;
 
 -- Track that we have updated the attributes stats in pg_statistic when finished
@@ -238,7 +265,9 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 'skip', '', '', 'autosta
 SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '', 'autostatstbl', 1, -1, 0, 1);
 
 2: INSERT INTO autostatstbl select i from generate_series(2001, 3000) as i;
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
+-- Disconnect the writer to flush pending statistics to the collector.
+2q:
+SELECT wait_for_analyze_stats('autostatstbl', 2, 2, 1000);
 
 -- auto_stats should not executed and auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
@@ -255,11 +284,14 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'reset', 1);
 SELECT gp_wait_until_triggered_fault('analyze_finished_one_relation', 1, 1);
 SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
 
+SELECT wait_for_analyze_stats('autostatstbl', 2, 3, 0);
 -- we can see the auto-ANALYZE finished, check statistic and analyze count
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
 -- expect analyze_count = 2, autoanalyze_count = 3, and n_mod_since_analyze = 0 since ANALYZE executed
 select analyze_count, autoanalyze_count, n_mod_since_analyze from pg_stat_all_tables_internal where relname = 'autostatstbl';
 
+DROP FUNCTION wait_for_analyze_stats(regclass, bigint, bigint, bigint);
+
 -- Reset GUCs.
 ALTER SYSTEM RESET autovacuum_naptime;
 select * from pg_reload_conf();
diff --git a/src/test/isolation2/output/autovacuum-analyze.source b/src/test/isolation2/output/autovacuum-analyze.source
index e071f9e7007..c4f0c53067a 100644
--- a/src/test/isolation2/output/autovacuum-analyze.source
+++ b/src/test/isolation2/output/autovacuum-analyze.source
@@ -384,6 +384,10 @@ END
 -- src/backend/postmaster/README.auto-ANALYZE.
 --
 
+-- Refresh the collector snapshot until the expected counters are visible.
+CREATE FUNCTION wait_for_analyze_stats(rel regclass, expected_analyze bigint, expected_autoanalyze bigint, expected_mod bigint) RETURNS bool AS $$ DECLARE retries int; /* in func */ BEGIN FOR retries IN 1..600 LOOP PERFORM pg_stat_clear_snapshot(); /* in func */ IF pg_stat_get_analyze_count(rel) = expected_analyze AND pg_stat_get_autoanalyze_count(rel) = expected_autoanalyze AND pg_stat_get_mod_since_analyze(rel) = expected_mod THEN RETURN true; /* in func */ END IF; /* in func */ PERFOR [...]
+CREATE
+
 -- Case 1 --
 2: SET gp_autostats_mode = 'on_no_stats';
 SET
@@ -408,7 +412,14 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '
 -- with auto_stats, the auto-ANALYZE still trigger
 2: INSERT INTO autostatstbl select i from generate_series(1, 1000) as i;
 INSERT 1000
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. 
+-- Disconnect the writer to flush pending statistics to the collector.
+2q: ... <quitting>
+SELECT wait_for_analyze_stats('autostatstbl', 1, 0, 1000);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
+
 -- auto_stats executed but auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 SELECT count(*) FROM pg_statistic where starelid = 'autostatstbl'::regclass;
  count 
@@ -458,6 +469,11 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
  Success:        
 (1 row)
 
+SELECT wait_for_analyze_stats('autostatstbl', 1, 1, 0);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
 -- we can see the auto-ANALYZE finished
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
  relpages | reltuples 
@@ -493,7 +509,14 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '
 
 2: INSERT INTO autostatstbl select i from generate_series(1001, 2000) as i;
 INSERT 1000
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. 
+-- Disconnect the writer to flush pending statistics to the collector.
+2q: ... <quitting>
+SELECT wait_for_analyze_stats('autostatstbl', 2, 1, 1000);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
+
 -- auto_stats executed but auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
  relpages | reltuples 
@@ -538,6 +561,11 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
  Success:        
 (1 row)
 
+SELECT wait_for_analyze_stats('autostatstbl', 2, 2, 0);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
 -- we can see the auto-ANALYZE finished, check statistic and analyze count
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
  relpages | reltuples 
@@ -554,6 +582,8 @@ select analyze_count, autoanalyze_count, n_mod_since_analyze from pg_stat_all_ta
 
 -- Case 3 --
 -- reset the threshold, so auto_stats will not be triggered and then auto-ANALYZE will be triggered.
+2: SET gp_autostats_mode = 'on_change';
+SET
 2: RESET gp_autostats_on_change_threshold;
 RESET
 
@@ -572,7 +602,14 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '
 
 2: INSERT INTO autostatstbl select i from generate_series(2001, 3000) as i;
 INSERT 1000
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. 
+-- Disconnect the writer to flush pending statistics to the collector.
+2q: ... <quitting>
+SELECT wait_for_analyze_stats('autostatstbl', 2, 2, 1000);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
+
 -- auto_stats should not executed and auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
  relpages | reltuples 
@@ -617,6 +654,11 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
  Success:        
 (1 row)
 
+SELECT wait_for_analyze_stats('autostatstbl', 2, 3, 0);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
 -- we can see the auto-ANALYZE finished, check statistic and analyze count
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
  relpages | reltuples 
@@ -630,6 +672,9 @@ select analyze_count, autoanalyze_count, n_mod_since_analyze from pg_stat_all_ta
  2             | 3                 | 0                   
 (1 row)
 
+DROP FUNCTION wait_for_analyze_stats(regclass, bigint, bigint, bigint);
+DROP
+
 -- Reset GUCs.
 ALTER SYSTEM RESET autovacuum_naptime;
 ALTER
diff --git a/src/test/singlenode_isolation2/input/autovacuum-analyze.source b/src/test/singlenode_isolation2/input/autovacuum-analyze.source
index b35c3072a85..c4afd59d7a5 100644
--- a/src/test/singlenode_isolation2/input/autovacuum-analyze.source
+++ b/src/test/singlenode_isolation2/input/autovacuum-analyze.source
@@ -157,6 +157,26 @@ select relpages, reltuples from pg_class where oid = 'anaabort'::regclass;
 -- src/backend/postmaster/README.auto-ANALYZE.
 --
 
+-- Refresh the collector snapshot until the expected counters are visible.
+CREATE FUNCTION wait_for_analyze_stats(rel regclass, expected_analyze bigint,
+                                      expected_autoanalyze bigint, expected_mod bigint)
+RETURNS bool AS $$
+DECLARE
+    retries int; /* in func */
+BEGIN
+    FOR retries IN 1..600 LOOP
+        PERFORM pg_stat_clear_snapshot(); /* in func */
+        IF pg_stat_get_analyze_count(rel) = expected_analyze
+           AND pg_stat_get_autoanalyze_count(rel) = expected_autoanalyze
+           AND pg_stat_get_mod_since_analyze(rel) = expected_mod THEN
+            RETURN true; /* in func */
+        END IF; /* in func */
+        PERFORM pg_sleep(0.1); /* in func */
+    END LOOP; /* in func */
+    RAISE EXCEPTION 'timed out waiting for analyze statistics for %', rel; /* in func */
+END; /* in func */
+$$ LANGUAGE plpgsql;
+
 -- Case 1 --
 2: SET gp_autostats_mode = 'on_no_stats';
 
@@ -170,7 +190,9 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '
 
 -- with auto_stats, the auto-ANALYZE still trigger
 2: INSERT INTO autostatstbl select i from generate_series(1, 1000) as i;
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
+-- Disconnect the writer to flush pending statistics to the collector.
+2q:
+SELECT wait_for_analyze_stats('autostatstbl', 1, 0, 1000);
 
 -- auto_stats executed but auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 SELECT count(*) FROM pg_statistic where starelid = 'autostatstbl'::regclass;
@@ -188,6 +210,7 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'reset', 1);
 SELECT gp_wait_until_triggered_fault('analyze_finished_one_relation', 1, 1);
 SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
 
+SELECT wait_for_analyze_stats('autostatstbl', 1, 1, 0);
 -- we can see the auto-ANALYZE finished
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
 -- expect analyze_count = 1, autoanalyze_count = 1, and n_mod_since_analyze = 0
@@ -204,7 +227,9 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 'skip', '', '', 'autosta
 SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '', 'autostatstbl', 1, -1, 0, 1);
 
 2: INSERT INTO autostatstbl select i from generate_series(1001, 2000) as i;
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
+-- Disconnect the writer to flush pending statistics to the collector.
+2q:
+SELECT wait_for_analyze_stats('autostatstbl', 2, 1, 1000);
 
 -- auto_stats executed but auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
@@ -221,6 +246,7 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'reset', 1);
 SELECT gp_wait_until_triggered_fault('analyze_finished_one_relation', 1, 1);
 SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
 
+SELECT wait_for_analyze_stats('autostatstbl', 2, 2, 0);
 -- we can see the auto-ANALYZE finished, check statistic and analyze count
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
 -- expect analyze_count = 2, autoanalyze_count = 2, and n_mod_since_analyze = 0
@@ -229,6 +255,7 @@ select analyze_count, autoanalyze_count, n_mod_since_analyze from pg_stat_all_ta
 
 -- Case 3 --
 -- reset the threshold, so auto_stats will not be triggered and then auto-ANALYZE will be triggered.
+2: SET gp_autostats_mode = 'on_change';
 2: RESET gp_autostats_on_change_threshold;
 
 -- Track that we have updated the attributes stats in pg_statistic when finished
@@ -237,7 +264,9 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 'skip', '', '', 'autosta
 SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '', 'autostatstbl', 1, -1, 0, 1);
 
 2: INSERT INTO autostatstbl select i from generate_series(2001, 3000) as i;
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
+-- Disconnect the writer to flush pending statistics to the collector.
+2q:
+SELECT wait_for_analyze_stats('autostatstbl', 2, 2, 1000);
 
 -- auto_stats should not executed and auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
@@ -254,11 +283,14 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'reset', 1);
 SELECT gp_wait_until_triggered_fault('analyze_finished_one_relation', 1, 1);
 SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
 
+SELECT wait_for_analyze_stats('autostatstbl', 2, 3, 0);
 -- we can see the auto-ANALYZE finished, check statistic and analyze count
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
 -- expect analyze_count = 2, autoanalyze_count = 3, and n_mod_since_analyze = 0 since ANALYZE executed
 select analyze_count, autoanalyze_count, n_mod_since_analyze from pg_stat_all_tables_internal where relname = 'autostatstbl';
 
+DROP FUNCTION wait_for_analyze_stats(regclass, bigint, bigint, bigint);
+
 -- Reset GUCs.
 ALTER SYSTEM RESET autovacuum_naptime;
 select * from pg_reload_conf();
diff --git a/src/test/singlenode_isolation2/output/autovacuum-analyze.source b/src/test/singlenode_isolation2/output/autovacuum-analyze.source
index 382b443944c..7d2c6e035b5 100644
--- a/src/test/singlenode_isolation2/output/autovacuum-analyze.source
+++ b/src/test/singlenode_isolation2/output/autovacuum-analyze.source
@@ -379,6 +379,10 @@ END
 -- src/backend/postmaster/README.auto-ANALYZE.
 --
 
+-- Refresh the collector snapshot until the expected counters are visible.
+CREATE FUNCTION wait_for_analyze_stats(rel regclass, expected_analyze bigint, expected_autoanalyze bigint, expected_mod bigint) RETURNS bool AS $$ DECLARE retries int; /* in func */ BEGIN FOR retries IN 1..600 LOOP PERFORM pg_stat_clear_snapshot(); /* in func */ IF pg_stat_get_analyze_count(rel) = expected_analyze AND pg_stat_get_autoanalyze_count(rel) = expected_autoanalyze AND pg_stat_get_mod_since_analyze(rel) = expected_mod THEN RETURN true; /* in func */ END IF; /* in func */ PERFOR [...]
+CREATE
+
 -- Case 1 --
 2: SET gp_autostats_mode = 'on_no_stats';
 SET
@@ -403,7 +407,14 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '
 -- with auto_stats, the auto-ANALYZE still trigger
 2: INSERT INTO autostatstbl select i from generate_series(1, 1000) as i;
 INSERT 1000
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. 
+-- Disconnect the writer to flush pending statistics to the collector.
+2q: ... <quitting>
+SELECT wait_for_analyze_stats('autostatstbl', 1, 0, 1000);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
+
 -- auto_stats executed but auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 SELECT count(*) FROM pg_statistic where starelid = 'autostatstbl'::regclass;
  count 
@@ -453,6 +464,11 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
  Success:        
 (1 row)
 
+SELECT wait_for_analyze_stats('autostatstbl', 1, 1, 0);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
 -- we can see the auto-ANALYZE finished
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
  relpages | reltuples 
@@ -488,7 +504,14 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '
 
 2: INSERT INTO autostatstbl select i from generate_series(1001, 2000) as i;
 INSERT 1000
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. 
+-- Disconnect the writer to flush pending statistics to the collector.
+2q: ... <quitting>
+SELECT wait_for_analyze_stats('autostatstbl', 2, 1, 1000);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
+
 -- auto_stats executed but auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
  relpages | reltuples 
@@ -533,6 +556,11 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
  Success:        
 (1 row)
 
+SELECT wait_for_analyze_stats('autostatstbl', 2, 2, 0);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
 -- we can see the auto-ANALYZE finished, check statistic and analyze count
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
  relpages | reltuples 
@@ -549,6 +577,8 @@ select analyze_count, autoanalyze_count, n_mod_since_analyze from pg_stat_all_ta
 
 -- Case 3 --
 -- reset the threshold, so auto_stats will not be triggered and then auto-ANALYZE will be triggered.
+2: SET gp_autostats_mode = 'on_change';
+SET
 2: RESET gp_autostats_on_change_threshold;
 RESET
 
@@ -567,7 +597,14 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '
 
 2: INSERT INTO autostatstbl select i from generate_series(2001, 3000) as i;
 INSERT 1000
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. 
+-- Disconnect the writer to flush pending statistics to the collector.
+2q: ... <quitting>
+SELECT wait_for_analyze_stats('autostatstbl', 2, 2, 1000);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
+
 -- auto_stats should not executed and auto-ANALYZE not execute yet since we suspend before finish ANALYZE.
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
  relpages | reltuples 
@@ -612,6 +649,11 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
  Success:        
 (1 row)
 
+SELECT wait_for_analyze_stats('autostatstbl', 2, 3, 0);
+ wait_for_analyze_stats 
+------------------------
+ t                      
+(1 row)
 -- we can see the auto-ANALYZE finished, check statistic and analyze count
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
  relpages | reltuples 
@@ -625,6 +667,9 @@ select analyze_count, autoanalyze_count, n_mod_since_analyze from pg_stat_all_ta
  2             | 3                 | 0                   
 (1 row)
 
+DROP FUNCTION wait_for_analyze_stats(regclass, bigint, bigint, bigint);
+DROP
+
 -- Reset GUCs.
 ALTER SYSTEM RESET autovacuum_naptime;
 ALTER


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cloudberry.apache.org
For additional commands, e-mail: commits-help@cloudberry.apache.org