From 0199255c67c2ab8b63418c1c5ccef5b6aa1396f2 Mon Sep 17 00:00:00 2001 From: Bertrand Drouvot Date: Thu, 5 Feb 2026 05:54:34 +0000 Subject: [PATCH v9 2/5] Add anytime flush tests for custom stats --- .../test_custom_stats/t/001_custom_stats.pl | 41 +++++++++++++ .../test_custom_fixed_stats--1.0.sql | 5 ++ .../test_custom_fixed_stats.c | 57 +++++++++++++++++++ .../test_custom_var_stats--1.0.sql | 5 ++ .../test_custom_stats/test_custom_var_stats.c | 27 +++++++++ 5 files changed, 135 insertions(+) 33.8% src/test/modules/test_custom_stats/t/ 66.1% src/test/modules/test_custom_stats/ diff --git a/src/test/modules/test_custom_stats/t/001_custom_stats.pl b/src/test/modules/test_custom_stats/t/001_custom_stats.pl index 9e6a7a38577..7be1b281776 100644 --- a/src/test/modules/test_custom_stats/t/001_custom_stats.pl +++ b/src/test/modules/test_custom_stats/t/001_custom_stats.pl @@ -156,5 +156,46 @@ $result = $node->safe_psql('postgres', ); is($result, "0", "report of fixed-sized after manual reset"); +# Test FLUSH_ANYTIME mechanism with custom fixed stats +# This verifies that custom stats can be flushed during a transaction + +# Reset stats first +$node->safe_psql('postgres', q(select test_custom_stats_fixed_reset())); +$node->safe_psql('postgres', q(select pg_stat_force_next_flush())); + +my $anytime_test = q[ + BEGIN; + -- Accumulate stats + select test_custom_stats_fixed_anytime_update() from generate_series(1, 2); + -- Wait (has to be greater than PGSTAT_MIN_INTERVAL) + select pg_sleep(1.5); + -- Check + select 'anytime:'||numcalls from test_custom_stats_fixed_report(); +]; + +$result = $node->safe_psql('postgres', $anytime_test); +like($result, qr/^anytime:2/m, + "anytime fixed stats flushed during transaction"); + +# Test FLUSH_ANYTIME mechanism with custom variable stats +# This verifies that custom stats can be flushed during a transaction + +$node->safe_psql('postgres', q(select pg_stat_force_next_flush())); + +$anytime_test = q[ + BEGIN; + -- Accumulate stats + select test_custom_stats_var_anytime_update('entry2'); + select test_custom_stats_var_anytime_update('entry2'); + -- Wait (has to be greater than PGSTAT_MIN_INTERVAL) + select pg_sleep(1.5); + -- Check + select * from test_custom_stats_var_report('entry2'); +]; + +$result = $node->safe_psql('postgres', $anytime_test); +like($result, qr/^entry2|2|/m, + "anytime var stats flushed during transaction"); + # Test completed successfully done_testing(); diff --git a/src/test/modules/test_custom_stats/test_custom_fixed_stats--1.0.sql b/src/test/modules/test_custom_stats/test_custom_fixed_stats--1.0.sql index 69a93b5241f..da3a798f289 100644 --- a/src/test/modules/test_custom_stats/test_custom_fixed_stats--1.0.sql +++ b/src/test/modules/test_custom_stats/test_custom_fixed_stats--1.0.sql @@ -18,3 +18,8 @@ CREATE FUNCTION test_custom_stats_fixed_reset() RETURNS void AS 'MODULE_PATHNAME', 'test_custom_stats_fixed_reset' LANGUAGE C STRICT PARALLEL UNSAFE; + +CREATE FUNCTION test_custom_stats_fixed_anytime_update() +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT PARALLEL UNSAFE; diff --git a/src/test/modules/test_custom_stats/test_custom_fixed_stats.c b/src/test/modules/test_custom_stats/test_custom_fixed_stats.c index 908bd18a7c7..30b0fbcbdc7 100644 --- a/src/test/modules/test_custom_stats/test_custom_fixed_stats.c +++ b/src/test/modules/test_custom_stats/test_custom_fixed_stats.c @@ -18,6 +18,7 @@ #include "pgstat.h" #include "utils/builtins.h" #include "utils/pgstat_internal.h" +#include "utils/timeout.h" PG_MODULE_MAGIC_EXT( .name = "test_custom_fixed_stats", @@ -43,11 +44,13 @@ typedef struct PgStatShared_CustomFixedEntry static void test_custom_stats_fixed_init_shmem_cb(void *stats); static void test_custom_stats_fixed_reset_all_cb(TimestampTz ts); static void test_custom_stats_fixed_snapshot_cb(void); +static bool test_custom_stats_fixed_flush_cb(bool nowait, bool anytime_only); static const PgStat_KindInfo custom_stats = { .name = "test_custom_fixed_stats", .fixed_amount = true, /* exactly one entry */ .write_to_file = true, /* persist to stats file */ + .flush_mode = FLUSH_ANYTIME, /* can be flushed anytime */ .shared_size = sizeof(PgStat_StatCustomFixedEntry), .shared_data_off = offsetof(PgStatShared_CustomFixedEntry, stats), @@ -56,8 +59,12 @@ static const PgStat_KindInfo custom_stats = { .init_shmem_cb = test_custom_stats_fixed_init_shmem_cb, .reset_all_cb = test_custom_stats_fixed_reset_all_cb, .snapshot_cb = test_custom_stats_fixed_snapshot_cb, + .flush_static_cb = test_custom_stats_fixed_flush_cb, }; +/* Pending statistics */ +static PgStat_StatCustomFixedEntry PendingCustomStats = {0}; + /* * Kind ID for test_custom_fixed_stats. */ @@ -141,6 +148,38 @@ test_custom_stats_fixed_snapshot_cb(void) #undef FIXED_COMP } +/* + * test_custom_stats_fixed_flush_cb + * Flush pending stats to shared memory + */ +static bool +test_custom_stats_fixed_flush_cb(bool nowait, bool anytime_only) +{ + PgStatShared_CustomFixedEntry *stats_shmem; + + /* Nothing to flush if no calls were made */ + if (PendingCustomStats.numcalls == 0) + return false; + + stats_shmem = pgstat_get_custom_shmem_data(PGSTAT_KIND_TEST_CUSTOM_FIXED_STATS); + + if (!nowait) + LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE); + else if (!LWLockConditionalAcquire(&stats_shmem->lock, LW_EXCLUSIVE)) + return true; + + pgstat_begin_changecount_write(&stats_shmem->changecount); + stats_shmem->stats.numcalls += PendingCustomStats.numcalls; + pgstat_end_changecount_write(&stats_shmem->changecount); + + LWLockRelease(&stats_shmem->lock); + + /* Reset pending stats */ + PendingCustomStats.numcalls = 0; + + return false; /* successfully flushed */ +} + /*-------------------------------------------------------------------------- * SQL-callable functions *-------------------------------------------------------------------------- @@ -222,3 +261,21 @@ test_custom_stats_fixed_report(PG_FUNCTION_ARGS) /* Return as tuple */ PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls))); } + +/* + * test_custom_stats_fixed_anytime_update + * Increment call counter and schedule anytime flush + */ +PG_FUNCTION_INFO_V1(test_custom_stats_fixed_anytime_update); +Datum +test_custom_stats_fixed_anytime_update(PG_FUNCTION_ARGS) +{ + /* Accumulate in pending stats */ + PendingCustomStats.numcalls++; + + /* Schedule anytime stats update */ + pgstat_schedule_anytime_update(); + pgstat_report_fixed = true; + + PG_RETURN_VOID(); +} diff --git a/src/test/modules/test_custom_stats/test_custom_var_stats--1.0.sql b/src/test/modules/test_custom_stats/test_custom_var_stats--1.0.sql index 5ed8cfc2dcf..ed66d38981e 100644 --- a/src/test/modules/test_custom_stats/test_custom_var_stats--1.0.sql +++ b/src/test/modules/test_custom_stats/test_custom_var_stats--1.0.sql @@ -24,3 +24,8 @@ CREATE FUNCTION test_custom_stats_var_report(INOUT name TEXT, RETURNS SETOF record AS 'MODULE_PATHNAME', 'test_custom_stats_var_report' LANGUAGE C STRICT PARALLEL UNSAFE; + +CREATE FUNCTION test_custom_stats_var_anytime_update(IN name TEXT) +RETURNS void +AS 'MODULE_PATHNAME', 'test_custom_stats_var_anytime_update' +LANGUAGE C STRICT PARALLEL UNSAFE; diff --git a/src/test/modules/test_custom_stats/test_custom_var_stats.c b/src/test/modules/test_custom_stats/test_custom_var_stats.c index bc0b5d6e0eb..207e841911b 100644 --- a/src/test/modules/test_custom_stats/test_custom_var_stats.c +++ b/src/test/modules/test_custom_stats/test_custom_var_stats.c @@ -17,6 +17,7 @@ #include "storage/dsm_registry.h" #include "utils/builtins.h" #include "utils/pgstat_internal.h" +#include "utils/timeout.h" PG_MODULE_MAGIC_EXT( .name = "test_custom_var_stats", @@ -107,6 +108,7 @@ static const PgStat_KindInfo custom_stats = { .name = "test_custom_var_stats", .fixed_amount = false, /* variable number of entries */ .write_to_file = true, /* persist across restarts */ + .flush_mode = FLUSH_ANYTIME, /* can be flushed anytime */ .track_entry_count = true, /* count active entries */ .accessed_across_databases = true, /* global statistics */ .shared_size = sizeof(PgStatShared_CustomVarEntry), @@ -689,3 +691,28 @@ test_custom_stats_var_report(PG_FUNCTION_ARGS) SRF_RETURN_DONE(funcctx); } + +/* + * test_custom_stats_var_anytime_update + * Increment custom statistic counter and schedule anytime flush + */ +PG_FUNCTION_INFO_V1(test_custom_stats_var_anytime_update); +Datum +test_custom_stats_var_anytime_update(PG_FUNCTION_ARGS) +{ + char *stat_name = text_to_cstring(PG_GETARG_TEXT_PP(0)); + PgStat_EntryRef *entry_ref; + PgStat_StatCustomVarEntry *pending_entry; + + /* Get pending entry in local memory */ + entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_TEST_CUSTOM_VAR_STATS, InvalidOid, + PGSTAT_CUSTOM_VAR_STATS_IDX(stat_name), NULL); + + pending_entry = (PgStat_StatCustomVarEntry *) entry_ref->pending; + pending_entry->numcalls++; + + /* Schedule anytime stats update */ + pgstat_schedule_anytime_update(); + + PG_RETURN_VOID(); +} -- 2.34.1