From 68b7235559bc71aab7a959b6c7ca43a877f3024a Mon Sep 17 00:00:00 2001 From: Sami Imseih Date: Sun, 10 May 2026 07:06:04 -0500 Subject: [PATCH 1/2] pgstat: add pgstat_flush_pending() and pg_stat_flush_pending(pid) Add infrastructure for flushing pending statistics to shared memory on demand, both locally and across backends. pgstat_flush_pending() flushes all pending stats entries in the calling backend immediately. Unlike pgstat_report_stat(), it can be called mid-transaction, making it suitable for view functions that need fresh shared stats before the transaction ends. pg_stat_flush_pending(pid) is the SQL-callable interface: if the target PID is the caller's own backend, it flushes immediately; otherwise it signals the target backend via PROCSIG_FLUSH_STATS to flush at its next CHECK_FOR_INTERRUPTS() call. This addresses the visibility gap where pending stats accumulated in other backends are not reflected in shared memory views until the next idle flush or transaction end. --- src/backend/storage/ipc/procsignal.c | 3 ++ src/backend/tcop/postgres.c | 3 ++ src/backend/utils/activity/pgstat.c | 60 ++++++++++++++++++++++++++++ src/backend/utils/adt/pgstatfuncs.c | 40 +++++++++++++++++++ src/backend/utils/init/globals.c | 1 + src/include/catalog/pg_proc.dat | 6 +++ src/include/miscadmin.h | 1 + src/include/pgstat.h | 3 ++ src/include/storage/procsignal.h | 1 + 9 files changed, 118 insertions(+) diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c index 264e4c22ca6..6e0f318c42d 100644 --- a/src/backend/storage/ipc/procsignal.c +++ b/src/backend/storage/ipc/procsignal.c @@ -711,6 +711,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS) if (CheckProcSignal(PROCSIG_REPACK_MESSAGE)) HandleRepackMessageInterrupt(); + if (CheckProcSignal(PROCSIG_FLUSH_STATS)) + HandleFlushStatsInterrupt(); + if (CheckProcSignal(PROCSIG_SLOTSYNC_MESSAGE)) HandleSlotSyncMessageInterrupt(); diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index dbef734a93f..4749511235d 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -3609,6 +3609,9 @@ ProcessInterrupts(void) if (LogMemoryContextPending) ProcessLogMemoryContextInterrupt(); + if (FlushStatsPending) + ProcessFlushStatsInterrupt(); + if (ParallelApplyMessagePending) ProcessParallelApplyMessages(); diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c index b67da88c7dc..0fbdf7e3bca 100644 --- a/src/backend/utils/activity/pgstat.c +++ b/src/backend/utils/activity/pgstat.c @@ -106,6 +106,7 @@ #include "access/xact.h" #include "lib/dshash.h" +#include "miscadmin.h" #include "pgstat.h" #include "storage/fd.h" #include "storage/ipc.h" @@ -845,6 +846,65 @@ pgstat_force_next_flush(void) pgStatForceNextFlush = true; } +/* + * Immediately flush all pending statistics entries to shared memory. + * + * Unlike pgstat_report_stat(), this can be called mid-transaction. + * It is useful for code that needs pending stats visible in shared memory + * before the transaction ends (e.g., view functions that read shared stats). + */ +void +pgstat_flush_pending(void) +{ + dlist_mutable_iter iter; + + if (dlist_is_empty(&pgStatPending)) + return; + + dlist_foreach_modify(iter, &pgStatPending) + { + PgStat_EntryRef *entry_ref = + dlist_container(PgStat_EntryRef, pending_node, iter.cur); + PgStat_Kind kind = entry_ref->shared_entry->key.kind; + const PgStat_KindInfo *kind_info; + + kind_info = pgstat_get_kind_info(kind); + if (kind_info->flush_pending_cb && + kind_info->flush_pending_cb(entry_ref, false)) + pgstat_delete_pending_entry(entry_ref); + } +} + +/* + * HandleFlushStatsInterrupt + * Handle receipt of a signal indicating that pending stats should be + * flushed to shared memory. + * + * The actual flush is deferred to ProcessFlushStatsInterrupt(), called from + * CHECK_FOR_INTERRUPTS(). + */ +void +HandleFlushStatsInterrupt(void) +{ + InterruptPending = true; + FlushStatsPending = true; + /* latch will be set by procsignal_sigusr1_handler */ +} + +/* + * ProcessFlushStatsInterrupt + * Flush all pending statistics to shared memory. + * + * Called from CHECK_FOR_INTERRUPTS() when FlushStatsPending is set. + */ +void +ProcessFlushStatsInterrupt(void) +{ + FlushStatsPending = false; + + pgstat_flush_pending(); +} + /* * Only for use by pgstat_reset_counters() */ diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 7a9dfa9ba3b..00ddedc2e95 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -28,6 +28,7 @@ #include "replication/logicallauncher.h" #include "storage/proc.h" #include "storage/procarray.h" +#include "storage/procsignal.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/timestamp.h" @@ -1929,6 +1930,45 @@ pg_stat_force_next_flush(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } +/* + * Signal a backend to flush all its pending statistics to shared memory. + * If the target is the current backend, the flush happens immediately. + */ +Datum +pg_stat_flush_pending(PG_FUNCTION_ARGS) +{ + int pid = PG_GETARG_INT32(0); + PGPROC *proc; + ProcNumber procNumber = INVALID_PROC_NUMBER; + + if (pid == MyProcPid) + { + pgstat_flush_pending(); + PG_RETURN_BOOL(true); + } + + proc = BackendPidGetProc(pid); + if (proc == NULL) + proc = AuxiliaryPidGetProc(pid); + + if (proc == NULL) + { + ereport(WARNING, + (errmsg("PID %d is not a PostgreSQL server process", pid))); + PG_RETURN_BOOL(false); + } + + procNumber = GetNumberFromPGProc(proc); + if (SendProcSignal(pid, PROCSIG_FLUSH_STATS, procNumber) < 0) + { + ereport(WARNING, + (errmsg("could not send signal to process %d: %m", pid))); + PG_RETURN_BOOL(false); + } + + PG_RETURN_BOOL(true); +} + /* Reset all counters for the current database */ Datum diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c index bbd28d14d99..957d4507d04 100644 --- a/src/backend/utils/init/globals.c +++ b/src/backend/utils/init/globals.c @@ -39,6 +39,7 @@ volatile sig_atomic_t TransactionTimeoutPending = false; volatile sig_atomic_t IdleSessionTimeoutPending = false; volatile sig_atomic_t ProcSignalBarrierPending = false; volatile sig_atomic_t LogMemoryContextPending = false; +volatile sig_atomic_t FlushStatsPending = false; volatile sig_atomic_t IdleStatsUpdateTimeoutPending = false; volatile uint32 InterruptHoldoffCount = 0; volatile uint32 QueryCancelHoldoffCount = 0; diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index fa9ae79082b..daedafc6845 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -6209,6 +6209,12 @@ proname => 'pg_stat_force_next_flush', proisstrict => 'f', provolatile => 'v', proparallel => 'r', prorettype => 'void', proargtypes => '', prosrc => 'pg_stat_force_next_flush' }, +{ oid => '9953', + descr => 'statistics: flush pending stats of the specified backend to shared memory', + proname => 'pg_stat_flush_pending', provolatile => 'v', + prorettype => 'bool', proargtypes => 'int4', + prosrc => 'pg_stat_flush_pending', + proacl => '{POSTGRES=X}' }, { oid => '2274', descr => 'statistics: reset collected statistics for current database', proname => 'pg_stat_reset', proisstrict => 'f', provolatile => 'v', diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 8ccdf61246b..2205add6445 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -97,6 +97,7 @@ extern PGDLLIMPORT volatile sig_atomic_t TransactionTimeoutPending; extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending; extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending; extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending; +extern PGDLLIMPORT volatile sig_atomic_t FlushStatsPending; extern PGDLLIMPORT volatile sig_atomic_t IdleStatsUpdateTimeoutPending; extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending; diff --git a/src/include/pgstat.h b/src/include/pgstat.h index dfa2e837638..a19473f71d9 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -552,6 +552,9 @@ 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_flush_pending(void); +extern void HandleFlushStatsInterrupt(void); +extern void ProcessFlushStatsInterrupt(void); extern void pgstat_reset_counters(void); extern void pgstat_reset(PgStat_Kind kind, Oid dboid, uint64 objid); diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h index aaa158bfd66..5ce66aaeb9a 100644 --- a/src/include/storage/procsignal.h +++ b/src/include/storage/procsignal.h @@ -38,6 +38,7 @@ typedef enum PROCSIG_PARALLEL_APPLY_MESSAGE, /* Message from parallel apply workers */ PROCSIG_SLOTSYNC_MESSAGE, /* ask slot synchronization to stop */ PROCSIG_REPACK_MESSAGE, /* Message from repack worker */ + PROCSIG_FLUSH_STATS, /* ask backend to flush pending statistics */ PROCSIG_RECOVERY_CONFLICT, /* backend is blocking recovery, check * PGPROC->pendingRecoveryConflicts for the * reason */ -- 2.50.1 (Apple Git-155)