0001-pgstat-add-pgstat_flush_pending-and-pg_stat_flush_pe.patch
application/octet-stream
Filename: 0001-pgstat-add-pgstat_flush_pending-and-pg_stat_flush_pe.patch
Type: application/octet-stream
Part: 1
From 68b7235559bc71aab7a959b6c7ca43a877f3024a Mon Sep 17 00:00:00 2001
From: Sami Imseih <samimseih@gmail.com>
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)