From 0f96965c658147d6d6bad096d2d4a2c9c665f4a9 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Wed, 6 Apr 2022 23:35:56 -0700 Subject: [PATCH] pgstat: add pg_stat_force_next_flush(), use it to simplify tests. In the stats collector days it was hard to write tests for the stats system, because fundamentally delivery of stats messages over UDP was not synchronous (nor guaranteed). Now we easily can force pending stats updates to be flushed synchronously. This moves stats.sql into a parallel group, there isn't a reason for it to run in isolation anymore. And it may shake out some bugs. Bumps catversion. Author: Andres Freund Discussion: https://postgr.es/m/20220303021600.hs34ghqcw6zcokdh@alap3.anarazel.de --- contrib/test_decoding/expected/stats.out | 71 ++----------- contrib/test_decoding/sql/stats.sql | 59 ++--------- src/backend/utils/activity/pgstat.c | 23 +++++ src/backend/utils/adt/pgstatfuncs.c | 10 ++ src/include/catalog/pg_proc.dat | 5 + src/include/pgstat.h | 1 + src/test/regress/expected/brin.out | 22 ++++ src/test/regress/expected/stats.out | 123 +---------------------- src/test/regress/parallel_schedule | 5 +- src/test/regress/sql/brin.sql | 19 ++++ src/test/regress/sql/stats.sql | 119 +--------------------- 11 files changed, 103 insertions(+), 354 deletions(-) diff --git a/contrib/test_decoding/expected/stats.out b/contrib/test_decoding/expected/stats.out index 206c0a126e..d9aeaa4426 100644 --- a/contrib/test_decoding/expected/stats.out +++ b/contrib/test_decoding/expected/stats.out @@ -7,50 +7,6 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_stats', ' (1 row) CREATE TABLE stats_test(data text); --- function to wait for counters to advance -CREATE FUNCTION wait_for_decode_stats(check_reset bool, check_spill_txns bool) RETURNS void AS $$ -DECLARE - start_time timestamptz := clock_timestamp(); - updated bool; -BEGIN - -- we don't want to wait forever; loop will exit after 30 seconds - FOR i IN 1 .. 300 LOOP - - IF check_spill_txns THEN - - -- check to see if all updates have been reset/updated - SELECT CASE WHEN check_reset THEN (spill_txns = 0) - ELSE (spill_txns > 0) - END - INTO updated - FROM pg_stat_replication_slots WHERE slot_name='regression_slot_stats'; - - ELSE - - -- check to see if all updates have been reset/updated - SELECT CASE WHEN check_reset THEN (total_txns = 0) - ELSE (total_txns > 0) - END - INTO updated - FROM pg_stat_replication_slots WHERE slot_name='regression_slot_stats'; - - END IF; - - exit WHEN updated; - - -- wait a little - perform pg_sleep_for('100 milliseconds'); - - -- reset stats snapshot so we can test again - perform pg_stat_clear_snapshot(); - - END LOOP; - - -- report time waited in postmaster log (where it won't change test output) - RAISE LOG 'wait_for_decode_stats delayed % seconds', - extract(epoch from clock_timestamp() - start_time); -END -$$ LANGUAGE plpgsql; -- non-spilled xact SET logical_decoding_work_mem to '64MB'; INSERT INTO stats_test values(1); @@ -60,9 +16,9 @@ SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot_stats', NULL, 3 (1 row) -SELECT wait_for_decode_stats(false, false); - wait_for_decode_stats ------------------------ +SELECT pg_stat_force_next_flush(); + pg_stat_force_next_flush +-------------------------- (1 row) @@ -73,19 +29,13 @@ SELECT slot_name, spill_txns = 0 AS spill_txns, spill_count = 0 AS spill_count, (1 row) RESET logical_decoding_work_mem; --- reset the slot stats, and wait for stats collector's total txn to reset +-- reset the slot stats SELECT pg_stat_reset_replication_slot('regression_slot_stats'); pg_stat_reset_replication_slot -------------------------------- (1 row) -SELECT wait_for_decode_stats(true, false); - wait_for_decode_stats ------------------------ - -(1 row) - SELECT slot_name, spill_txns, spill_count, total_txns, total_bytes FROM pg_stat_replication_slots; slot_name | spill_txns | spill_count | total_txns | total_bytes -----------------------+------------+-------------+------------+------------- @@ -102,12 +52,12 @@ SELECT count(*) FROM pg_logical_slot_peek_changes('regression_slot_stats', NULL, 5002 (1 row) --- Check stats, wait for the stats collector to update. We can't test the --- exact stats count as that can vary if any background transaction (say by --- autovacuum) happens in parallel to the main transaction. -SELECT wait_for_decode_stats(false, true); - wait_for_decode_stats ------------------------ +-- Check stats. We can't test the exact stats count as that can vary if any +-- background transaction (say by autovacuum) happens in parallel to the main +-- transaction. +SELECT pg_stat_force_next_flush(); + pg_stat_force_next_flush +-------------------------- (1 row) @@ -133,7 +83,6 @@ SELECT slot_name FROM pg_stat_replication_slots; (1 row) COMMIT; -DROP FUNCTION wait_for_decode_stats(bool, bool); DROP TABLE stats_test; SELECT pg_drop_replication_slot('regression_slot_stats'); pg_drop_replication_slot diff --git a/contrib/test_decoding/sql/stats.sql b/contrib/test_decoding/sql/stats.sql index 67462ca27f..6592c36a4d 100644 --- a/contrib/test_decoding/sql/stats.sql +++ b/contrib/test_decoding/sql/stats.sql @@ -5,62 +5,16 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_stats', ' CREATE TABLE stats_test(data text); --- function to wait for counters to advance -CREATE FUNCTION wait_for_decode_stats(check_reset bool, check_spill_txns bool) RETURNS void AS $$ -DECLARE - start_time timestamptz := clock_timestamp(); - updated bool; -BEGIN - -- we don't want to wait forever; loop will exit after 30 seconds - FOR i IN 1 .. 300 LOOP - - IF check_spill_txns THEN - - -- check to see if all updates have been reset/updated - SELECT CASE WHEN check_reset THEN (spill_txns = 0) - ELSE (spill_txns > 0) - END - INTO updated - FROM pg_stat_replication_slots WHERE slot_name='regression_slot_stats'; - - ELSE - - -- check to see if all updates have been reset/updated - SELECT CASE WHEN check_reset THEN (total_txns = 0) - ELSE (total_txns > 0) - END - INTO updated - FROM pg_stat_replication_slots WHERE slot_name='regression_slot_stats'; - - END IF; - - exit WHEN updated; - - -- wait a little - perform pg_sleep_for('100 milliseconds'); - - -- reset stats snapshot so we can test again - perform pg_stat_clear_snapshot(); - - END LOOP; - - -- report time waited in postmaster log (where it won't change test output) - RAISE LOG 'wait_for_decode_stats delayed % seconds', - extract(epoch from clock_timestamp() - start_time); -END -$$ LANGUAGE plpgsql; - -- non-spilled xact SET logical_decoding_work_mem to '64MB'; INSERT INTO stats_test values(1); SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot_stats', NULL, NULL, 'skip-empty-xacts', '1'); -SELECT wait_for_decode_stats(false, false); +SELECT pg_stat_force_next_flush(); SELECT slot_name, spill_txns = 0 AS spill_txns, spill_count = 0 AS spill_count, total_txns > 0 AS total_txns, total_bytes > 0 AS total_bytes FROM pg_stat_replication_slots; RESET logical_decoding_work_mem; --- reset the slot stats, and wait for stats collector's total txn to reset +-- reset the slot stats SELECT pg_stat_reset_replication_slot('regression_slot_stats'); -SELECT wait_for_decode_stats(true, false); SELECT slot_name, spill_txns, spill_count, total_txns, total_bytes FROM pg_stat_replication_slots; -- spilling the xact @@ -69,10 +23,10 @@ INSERT INTO stats_test SELECT 'serialize-topbig--1:'||g.i FROM generate_series(1 COMMIT; SELECT count(*) FROM pg_logical_slot_peek_changes('regression_slot_stats', NULL, NULL, 'skip-empty-xacts', '1'); --- Check stats, wait for the stats collector to update. We can't test the --- exact stats count as that can vary if any background transaction (say by --- autovacuum) happens in parallel to the main transaction. -SELECT wait_for_decode_stats(false, true); +-- Check stats. We can't test the exact stats count as that can vary if any +-- background transaction (say by autovacuum) happens in parallel to the main +-- transaction. +SELECT pg_stat_force_next_flush(); SELECT slot_name, spill_txns > 0 AS spill_txns, spill_count > 0 AS spill_count FROM pg_stat_replication_slots; -- Ensure stats can be repeatedly accessed using the same stats snapshot. See @@ -82,6 +36,5 @@ SELECT slot_name FROM pg_stat_replication_slots; SELECT slot_name FROM pg_stat_replication_slots; COMMIT; -DROP FUNCTION wait_for_decode_stats(bool, bool); DROP TABLE stats_test; SELECT pg_drop_replication_slot('regression_slot_stats'); diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c index db30c72073..111869b211 100644 --- a/src/backend/utils/activity/pgstat.c +++ b/src/backend/utils/activity/pgstat.c @@ -219,6 +219,12 @@ static MemoryContext pgStatPendingContext = NULL; static dlist_head pgStatPending = DLIST_STATIC_INIT(pgStatPending); +/* + * Force the next stats flush to happen regardless of + * PGSTAT_MIN_INTERVAL. Useful in test scripts. + */ +static bool pgStatForceNextFlush = false; + /* * For assertions that check pgstat is not used before initialization / after * shutdown. @@ -560,6 +566,13 @@ pgstat_report_stat(bool force) pgstat_assert_is_up(); Assert(!IsTransactionBlock()); + /* "absorb" the forced flush even if there's nothing to flush */ + if (pgStatForceNextFlush) + { + force = true; + pgStatForceNextFlush = false; + } + /* Don't expend a clock check if nothing to do */ if (dlist_is_empty(&pgStatPending) && !have_slrustats && @@ -637,6 +650,16 @@ pgstat_report_stat(bool force) return 0; } +/* + * Force locally pending stats to be flushed during the next + * pgstat_report_stat() call. This is useful for writing tests. + */ +void +pgstat_force_next_flush(void) +{ + pgStatForceNextFlush = true; +} + /* * Only for use by pgstat_reset_counters() */ diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 248d318f86..5f1815727d 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -2067,6 +2067,16 @@ pg_stat_clear_snapshot(PG_FUNCTION_ARGS) } +/* Force statistics to be reported at the next occasion */ +Datum +pg_stat_force_next_flush(PG_FUNCTION_ARGS) +{ + pgstat_force_next_flush(); + + PG_RETURN_VOID(); +} + + /* Reset all counters for the current database */ Datum pg_stat_reset(PG_FUNCTION_ARGS) diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 0f0f41b2f9..2b4438bc3d 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -5744,6 +5744,11 @@ proname => 'pg_stat_clear_snapshot', proisstrict => 'f', provolatile => 'v', proparallel => 'r', prorettype => 'void', proargtypes => '', prosrc => 'pg_stat_clear_snapshot' }, +{ oid => '2137', + descr => 'statistics: force stats to be flushed after the next commit', + proname => 'pg_stat_force_next_flush', proisstrict => 'f', provolatile => 'v', + proparallel => 'r', prorettype => 'void', proargtypes => '', + prosrc => 'pg_stat_force_next_flush' }, { oid => '2274', descr => 'statistics: reset collected statistics for current database', proname => 'pg_stat_reset', proisstrict => 'f', provolatile => 'v', diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 8ac3860e8d..a0b853f4a6 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -414,6 +414,7 @@ extern void pgstat_initialize(void); /* Functions called from backends */ extern long pgstat_report_stat(bool force); +extern void pgstat_force_next_flush(void); extern void pgstat_reset_counters(void); extern void pgstat_reset(PgStat_Kind kind, Oid dboid, Oid objectid); diff --git a/src/test/regress/expected/brin.out b/src/test/regress/expected/brin.out index 2d03d8e134..ed7879f583 100644 --- a/src/test/regress/expected/brin.out +++ b/src/test/regress/expected/brin.out @@ -603,3 +603,25 @@ SELECT COUNT(*) FROM brin_hot_2 WHERE a = 2 AND b = 100; 1 (1 row) +-- test BRIN index doesn't block HOT update +CREATE TABLE brin_hot ( + id integer PRIMARY KEY, + val integer NOT NULL +) WITH (autovacuum_enabled = off, fillfactor = 70); +INSERT INTO brin_hot SELECT *, 0 FROM generate_series(1, 235); +CREATE INDEX val_brin ON brin_hot using brin(val); +UPDATE brin_hot SET val = -3 WHERE id = 42; +-- ensure pending stats are flushed +SELECT pg_stat_force_next_flush(); + pg_stat_force_next_flush +-------------------------- + +(1 row) + +SELECT pg_stat_get_tuples_hot_updated('brin_hot'::regclass::oid); + pg_stat_get_tuples_hot_updated +-------------------------------- + 1 +(1 row) + +DROP TABLE brin_hot; diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out index 64e2ff6b29..aa05b3b78a 100644 --- a/src/test/regress/expected/stats.out +++ b/src/test/regress/expected/stats.out @@ -28,63 +28,6 @@ SELECT t.seq_scan, t.seq_tup_read, t.idx_scan, t.idx_tup_fetch, pg_catalog.pg_statio_user_tables AS b WHERE t.relname='tenk2' AND b.relname='tenk2'; COMMIT; --- function to wait for counters to advance -create function wait_for_stats() returns void as $$ -declare - start_time timestamptz := clock_timestamp(); - updated1 bool; - updated2 bool; - updated3 bool; - updated4 bool; -begin - SET LOCAL stats_fetch_consistency = snapshot; - - -- We don't want to wait forever. No timeout suffices if the OS drops our - -- stats traffic because an earlier test file left a full UDP buffer. - -- Hence, don't use PG_TEST_TIMEOUT_DEFAULT, which may be large for - -- can't-happen timeouts. Exit after 30 seconds. - for i in 1 .. 300 loop - - -- With parallel query, the seqscan and indexscan on tenk2 might be done - -- in parallel worker processes, which will send their stats counters - -- asynchronously to what our own session does. So we must check for - -- those counts to be registered separately from the update counts. - - -- check to see if seqscan has been sensed - SELECT (st.seq_scan >= pr.seq_scan + 1) INTO updated1 - FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr - WHERE st.relname='tenk2' AND cl.relname='tenk2'; - - -- check to see if indexscan has been sensed - SELECT (st.idx_scan >= pr.idx_scan + 1) INTO updated2 - FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr - WHERE st.relname='tenk2' AND cl.relname='tenk2'; - - -- check to see if all updates have been sensed - SELECT (n_tup_ins > 0) INTO updated3 - FROM pg_stat_user_tables WHERE relname='trunc_stats_test4'; - - -- We must also check explicitly that pg_stat_get_snapshot_timestamp has - -- advanced, because that comes from the global stats file which might - -- be older than the per-DB stats file we got the other values from. - SELECT (pr.snap_ts < pg_stat_get_snapshot_timestamp()) INTO updated4 - FROM prevstats AS pr; - - exit when updated1 and updated2 and updated3 and updated4; - - -- wait a little - perform pg_sleep_for('100 milliseconds'); - - -- reset stats snapshot so we can test again - perform pg_stat_clear_snapshot(); - - end loop; - - -- report time waited in postmaster log (where it won't change test output) - raise log 'wait_for_stats delayed % seconds', - extract(epoch from clock_timestamp() - start_time); -end -$$ language plpgsql; -- test effects of TRUNCATE on n_live_tup/n_dead_tup counters CREATE TABLE trunc_stats_test(id serial); CREATE TABLE trunc_stats_test1(id serial, stuff text); @@ -153,17 +96,10 @@ SELECT count(*) FROM tenk2 WHERE unique1 = 1; (1 row) RESET enable_bitmapscan; --- We can't just call wait_for_stats() at this point, because we only --- transmit stats when the session goes idle, and we probably didn't --- transmit the last couple of counts yet thanks to the rate-limiting logic --- in pgstat_report_stat(). But instead of waiting for the rate limiter's --- timeout to elapse, let's just start a new session. The old one will --- then send its stats before dying. -\c - --- wait for stats collector to update -SELECT wait_for_stats(); - wait_for_stats ----------------- +-- ensure pending stats are flushed +SELECT pg_stat_force_next_flush(); + pg_stat_force_next_flush +-------------------------- (1 row) @@ -212,57 +148,6 @@ FROM prevstats AS pr; COMMIT; DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4; DROP TABLE prevstats; --- test BRIN index doesn't block HOT update - we include this test here, as it --- relies on statistics collector and so it may occasionally fail, especially --- on slower systems -CREATE TABLE brin_hot ( - id integer PRIMARY KEY, - val integer NOT NULL -) WITH (autovacuum_enabled = off, fillfactor = 70); -INSERT INTO brin_hot SELECT *, 0 FROM generate_series(1, 235); -CREATE INDEX val_brin ON brin_hot using brin(val); -CREATE FUNCTION wait_for_hot_stats() RETURNS void AS $$ -DECLARE - start_time timestamptz := clock_timestamp(); - updated bool; -BEGIN - -- we don't want to wait forever; loop will exit after 30 seconds - FOR i IN 1 .. 300 LOOP - SELECT (pg_stat_get_tuples_hot_updated('brin_hot'::regclass::oid) > 0) INTO updated; - EXIT WHEN updated; - - -- wait a little - PERFORM pg_sleep_for('100 milliseconds'); - -- reset stats snapshot so we can test again - PERFORM pg_stat_clear_snapshot(); - END LOOP; - -- report time waited in postmaster log (where it won't change test output) - RAISE log 'wait_for_hot_stats delayed % seconds', - EXTRACT(epoch FROM clock_timestamp() - start_time); -END -$$ LANGUAGE plpgsql; -UPDATE brin_hot SET val = -3 WHERE id = 42; --- We can't just call wait_for_hot_stats() at this point, because we only --- transmit stats when the session goes idle, and we probably didn't --- transmit the last couple of counts yet thanks to the rate-limiting logic --- in pgstat_report_stat(). But instead of waiting for the rate limiter's --- timeout to elapse, let's just start a new session. The old one will --- then send its stats before dying. -\c - -SELECT wait_for_hot_stats(); - wait_for_hot_stats --------------------- - -(1 row) - -SELECT pg_stat_get_tuples_hot_updated('brin_hot'::regclass::oid); - pg_stat_get_tuples_hot_updated --------------------------------- - 1 -(1 row) - -DROP TABLE brin_hot; -DROP FUNCTION wait_for_hot_stats(); -- ensure that stats accessors handle NULL input correctly SELECT pg_stat_get_replication_slot(NULL); pg_stat_get_replication_slot diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 5030d19c03..1087b2c14f 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -124,7 +124,7 @@ test: plancache limit plpgsql copy2 temp domain rangefuncs prepare conversion tr # ---------- # Another group of parallel tests # ---------- -test: partition_join partition_prune reloptions hash_part indexing partition_aggregate partition_info tuplesort explain compression memoize +test: partition_join partition_prune reloptions hash_part indexing partition_aggregate partition_info tuplesort explain compression memoize stats # event_trigger cannot run concurrently with any test that runs DDL # oidjoins is read-only, though, and should run late for best coverage @@ -132,6 +132,3 @@ test: event_trigger oidjoins # this test also uses event triggers, so likewise run it by itself test: fast_default - -# run stats by itself because its delay may be insufficient under heavy load -test: stats diff --git a/src/test/regress/sql/brin.sql b/src/test/regress/sql/brin.sql index e12f3a0df9..920e053249 100644 --- a/src/test/regress/sql/brin.sql +++ b/src/test/regress/sql/brin.sql @@ -526,3 +526,22 @@ SET enable_seqscan = off; EXPLAIN (COSTS OFF) SELECT * FROM brin_hot_2 WHERE a = 2 AND b = 100; SELECT COUNT(*) FROM brin_hot_2 WHERE a = 2 AND b = 100; + + +-- test BRIN index doesn't block HOT update +CREATE TABLE brin_hot ( + id integer PRIMARY KEY, + val integer NOT NULL +) WITH (autovacuum_enabled = off, fillfactor = 70); + +INSERT INTO brin_hot SELECT *, 0 FROM generate_series(1, 235); +CREATE INDEX val_brin ON brin_hot using brin(val); + +UPDATE brin_hot SET val = -3 WHERE id = 42; + +-- ensure pending stats are flushed +SELECT pg_stat_force_next_flush(); + +SELECT pg_stat_get_tuples_hot_updated('brin_hot'::regclass::oid); + +DROP TABLE brin_hot; diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql index 85a253bcd4..7c77c33121 100644 --- a/src/test/regress/sql/stats.sql +++ b/src/test/regress/sql/stats.sql @@ -27,64 +27,6 @@ SELECT t.seq_scan, t.seq_tup_read, t.idx_scan, t.idx_tup_fetch, WHERE t.relname='tenk2' AND b.relname='tenk2'; COMMIT; --- function to wait for counters to advance -create function wait_for_stats() returns void as $$ -declare - start_time timestamptz := clock_timestamp(); - updated1 bool; - updated2 bool; - updated3 bool; - updated4 bool; -begin - SET LOCAL stats_fetch_consistency = snapshot; - - -- We don't want to wait forever. No timeout suffices if the OS drops our - -- stats traffic because an earlier test file left a full UDP buffer. - -- Hence, don't use PG_TEST_TIMEOUT_DEFAULT, which may be large for - -- can't-happen timeouts. Exit after 30 seconds. - for i in 1 .. 300 loop - - -- With parallel query, the seqscan and indexscan on tenk2 might be done - -- in parallel worker processes, which will send their stats counters - -- asynchronously to what our own session does. So we must check for - -- those counts to be registered separately from the update counts. - - -- check to see if seqscan has been sensed - SELECT (st.seq_scan >= pr.seq_scan + 1) INTO updated1 - FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr - WHERE st.relname='tenk2' AND cl.relname='tenk2'; - - -- check to see if indexscan has been sensed - SELECT (st.idx_scan >= pr.idx_scan + 1) INTO updated2 - FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr - WHERE st.relname='tenk2' AND cl.relname='tenk2'; - - -- check to see if all updates have been sensed - SELECT (n_tup_ins > 0) INTO updated3 - FROM pg_stat_user_tables WHERE relname='trunc_stats_test4'; - - -- We must also check explicitly that pg_stat_get_snapshot_timestamp has - -- advanced, because that comes from the global stats file which might - -- be older than the per-DB stats file we got the other values from. - SELECT (pr.snap_ts < pg_stat_get_snapshot_timestamp()) INTO updated4 - FROM prevstats AS pr; - - exit when updated1 and updated2 and updated3 and updated4; - - -- wait a little - perform pg_sleep_for('100 milliseconds'); - - -- reset stats snapshot so we can test again - perform pg_stat_clear_snapshot(); - - end loop; - - -- report time waited in postmaster log (where it won't change test output) - raise log 'wait_for_stats delayed % seconds', - extract(epoch from clock_timestamp() - start_time); -end -$$ language plpgsql; - -- test effects of TRUNCATE on n_live_tup/n_dead_tup counters CREATE TABLE trunc_stats_test(id serial); CREATE TABLE trunc_stats_test1(id serial, stuff text); @@ -151,16 +93,8 @@ SET enable_bitmapscan TO off; SELECT count(*) FROM tenk2 WHERE unique1 = 1; RESET enable_bitmapscan; --- We can't just call wait_for_stats() at this point, because we only --- transmit stats when the session goes idle, and we probably didn't --- transmit the last couple of counts yet thanks to the rate-limiting logic --- in pgstat_report_stat(). But instead of waiting for the rate limiter's --- timeout to elapse, let's just start a new session. The old one will --- then send its stats before dying. -\c - - --- wait for stats collector to update -SELECT wait_for_stats(); +-- ensure pending stats are flushed +SELECT pg_stat_force_next_flush(); -- check effects BEGIN; @@ -190,55 +124,6 @@ COMMIT; DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4; DROP TABLE prevstats; --- test BRIN index doesn't block HOT update - we include this test here, as it --- relies on statistics collector and so it may occasionally fail, especially --- on slower systems -CREATE TABLE brin_hot ( - id integer PRIMARY KEY, - val integer NOT NULL -) WITH (autovacuum_enabled = off, fillfactor = 70); - -INSERT INTO brin_hot SELECT *, 0 FROM generate_series(1, 235); -CREATE INDEX val_brin ON brin_hot using brin(val); - -CREATE FUNCTION wait_for_hot_stats() RETURNS void AS $$ -DECLARE - start_time timestamptz := clock_timestamp(); - updated bool; -BEGIN - -- we don't want to wait forever; loop will exit after 30 seconds - FOR i IN 1 .. 300 LOOP - SELECT (pg_stat_get_tuples_hot_updated('brin_hot'::regclass::oid) > 0) INTO updated; - EXIT WHEN updated; - - -- wait a little - PERFORM pg_sleep_for('100 milliseconds'); - -- reset stats snapshot so we can test again - PERFORM pg_stat_clear_snapshot(); - END LOOP; - -- report time waited in postmaster log (where it won't change test output) - RAISE log 'wait_for_hot_stats delayed % seconds', - EXTRACT(epoch FROM clock_timestamp() - start_time); -END -$$ LANGUAGE plpgsql; - -UPDATE brin_hot SET val = -3 WHERE id = 42; - --- We can't just call wait_for_hot_stats() at this point, because we only --- transmit stats when the session goes idle, and we probably didn't --- transmit the last couple of counts yet thanks to the rate-limiting logic --- in pgstat_report_stat(). But instead of waiting for the rate limiter's --- timeout to elapse, let's just start a new session. The old one will --- then send its stats before dying. -\c - - -SELECT wait_for_hot_stats(); -SELECT pg_stat_get_tuples_hot_updated('brin_hot'::regclass::oid); - -DROP TABLE brin_hot; -DROP FUNCTION wait_for_hot_stats(); - - -- ensure that stats accessors handle NULL input correctly SELECT pg_stat_get_replication_slot(NULL); SELECT pg_stat_get_subscription_stats(NULL);