Hi all,

The last TODO item I had in my bucket about the generalization of
pgstats is the option to a better control on the flush of the stats
depending on the kind for fixed-numbered stats.  Currently, this is
controlled by pgstat_report_stat(), that includes special handling for
WAL, IO and SLRU stats, with two generic concepts:
- Check if there are pending entries, allowing a fast-path exit.
- Do the actual flush, with a recheck on pending entries.

SLRU and IO control that with one variable each, and WAL uses a
routine for the same called pgstat_have_pending_wal().  Please find
attached a patch to generalize the concept, with two new callbacks
that can be used for fixed-numbered stats.  SLRU, IO and WAL are
switched to use these (the two pgstat_flush_* routines have been kept
on purpose).  This brings some clarity in the code, by making
have_iostats and have_slrustats static in their respective files.  The
two pgstat_flush_* wrappers do not need a boolean as return result.

Running Postgres on scissors with a read-only workload that does not
trigger stats, I was not able to see a difference in runtime, but that
was on my own laptop, and I am planning to do more measurements on a
bigger machine.

This is in the same line of thoughts as the recent thread about the
backend init callback, generalizing more the whole facility:
https://www.postgresql.org/message-id/ztzr1k4pldewc...@paquier.xyz

Like the other one, I wanted to send that a few days ago, but well,
life likes going its own ways sometimes.

Thanks,
--
Michael
From e98f8d50c17cd8fc521bffb4bdc73ef58b7fa430 Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Tue, 3 Sep 2024 13:36:10 +0900
Subject: [PATCH] Add callbacks to control flush of fixed-numbered stats

This commit adds two callbacks in pgstats:
- have_fixed_pending_cb, to check if a stats kind has any pending stuff
waiting for a flush.
- flush_fixed_cb, to do the flush.

This is used by the SLRU, WAL and IO statistics, generalizing the
concept for all stats kinds (builtin and custom).
---
 src/include/utils/pgstat_internal.h      | 42 +++++++++-------
 src/backend/utils/activity/pgstat.c      | 63 +++++++++++++++++++-----
 src/backend/utils/activity/pgstat_io.c   | 22 ++++++++-
 src/backend/utils/activity/pgstat_slru.c | 13 ++++-
 src/backend/utils/activity/pgstat_wal.c  | 19 +++++--
 5 files changed, 119 insertions(+), 40 deletions(-)

diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index fb132e439d..d69aa05b1c 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -231,7 +231,7 @@ typedef struct PgStat_KindInfo
 
 	/*
 	 * For variable-numbered stats: flush pending stats. Required if pending
-	 * data is used.
+	 * data is used.  See flush_fixed_cb for fixed-numbered stats.
 	 */
 	bool		(*flush_pending_cb) (PgStat_EntryRef *sr, bool nowait);
 
@@ -259,6 +259,19 @@ typedef struct PgStat_KindInfo
 	 */
 	void		(*init_shmem_cb) (void *stats);
 
+	/*
+	 * For fixed-numbered statistics: Flush pending stats. Returns true if
+	 * some of the stats could not be flushed, due to lock contention for
+	 * example. Optional.
+	 */
+	bool		(*flush_fixed_cb) (bool nowait);
+
+	/*
+	 * For fixed-numbered statistics: Check for pending stats in need of
+	 * flush. Returns true if there are any stats pending for flush. Optional.
+	 */
+	bool		(*have_fixed_pending_cb) (void);
+
 	/*
 	 * For fixed-numbered statistics: Reset All.
 	 */
@@ -603,7 +616,10 @@ extern bool pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
  * Functions in pgstat_io.c
  */
 
-extern bool pgstat_flush_io(bool nowait);
+extern void pgstat_flush_io(bool nowait);
+
+extern bool pgstat_io_have_pending_cb(void);
+extern bool pgstat_io_flush_cb(bool nowait);
 extern void pgstat_io_init_shmem_cb(void *stats);
 extern void pgstat_io_reset_all_cb(TimestampTz ts);
 extern void pgstat_io_snapshot_cb(void);
@@ -662,7 +678,8 @@ extern PgStatShared_Common *pgstat_init_entry(PgStat_Kind kind,
  * Functions in pgstat_slru.c
  */
 
-extern bool pgstat_slru_flush(bool nowait);
+extern bool pgstat_slru_have_pending_cb(void);
+extern bool pgstat_slru_flush_cb(bool nowait);
 extern void pgstat_slru_init_shmem_cb(void *stats);
 extern void pgstat_slru_reset_all_cb(TimestampTz ts);
 extern void pgstat_slru_snapshot_cb(void);
@@ -672,10 +689,11 @@ extern void pgstat_slru_snapshot_cb(void);
  * Functions in pgstat_wal.c
  */
 
-extern bool pgstat_flush_wal(bool nowait);
 extern void pgstat_init_wal(void);
-extern bool pgstat_have_pending_wal(void);
+extern void pgstat_flush_wal(bool nowait);
 
+extern bool pgstat_wal_have_pending_cb(void);
+extern bool pgstat_wal_flush_cb(bool nowait);
 extern void pgstat_wal_init_shmem_cb(void *stats);
 extern void pgstat_wal_reset_all_cb(TimestampTz ts);
 extern void pgstat_wal_snapshot_cb(void);
@@ -705,20 +723,6 @@ extern void pgstat_create_transactional(PgStat_Kind kind, Oid dboid, Oid objoid)
 extern PGDLLIMPORT PgStat_LocalState pgStatLocal;
 
 
-/*
- * Variables in pgstat_io.c
- */
-
-extern PGDLLIMPORT bool have_iostats;
-
-
-/*
- * Variables in pgstat_slru.c
- */
-
-extern PGDLLIMPORT bool have_slrustats;
-
-
 /*
  * Implementation of inline functions declared above.
  */
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index b2ca3f39b7..8cbf706d5b 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -411,6 +411,8 @@ static const PgStat_KindInfo pgstat_kind_builtin_infos[PGSTAT_KIND_BUILTIN_SIZE]
 		.shared_data_off = offsetof(PgStatShared_IO, stats),
 		.shared_data_len = sizeof(((PgStatShared_IO *) 0)->stats),
 
+		.flush_fixed_cb = pgstat_io_flush_cb,
+		.have_fixed_pending_cb = pgstat_io_have_pending_cb,
 		.init_shmem_cb = pgstat_io_init_shmem_cb,
 		.reset_all_cb = pgstat_io_reset_all_cb,
 		.snapshot_cb = pgstat_io_snapshot_cb,
@@ -426,6 +428,8 @@ static const PgStat_KindInfo pgstat_kind_builtin_infos[PGSTAT_KIND_BUILTIN_SIZE]
 		.shared_data_off = offsetof(PgStatShared_SLRU, stats),
 		.shared_data_len = sizeof(((PgStatShared_SLRU *) 0)->stats),
 
+		.flush_fixed_cb = pgstat_slru_flush_cb,
+		.have_fixed_pending_cb = pgstat_slru_have_pending_cb,
 		.init_shmem_cb = pgstat_slru_init_shmem_cb,
 		.reset_all_cb = pgstat_slru_reset_all_cb,
 		.snapshot_cb = pgstat_slru_snapshot_cb,
@@ -441,6 +445,8 @@ static const PgStat_KindInfo pgstat_kind_builtin_infos[PGSTAT_KIND_BUILTIN_SIZE]
 		.shared_data_off = offsetof(PgStatShared_Wal, stats),
 		.shared_data_len = sizeof(((PgStatShared_Wal *) 0)->stats),
 
+		.flush_fixed_cb = pgstat_wal_flush_cb,
+		.have_fixed_pending_cb = pgstat_wal_have_pending_cb,
 		.init_shmem_cb = pgstat_wal_init_shmem_cb,
 		.reset_all_cb = pgstat_wal_reset_all_cb,
 		.snapshot_cb = pgstat_wal_snapshot_cb,
@@ -661,13 +667,37 @@ pgstat_report_stat(bool force)
 	}
 
 	/* Don't expend a clock check if nothing to do */
-	if (dlist_is_empty(&pgStatPending) &&
-		!have_iostats &&
-		!have_slrustats &&
-		!pgstat_have_pending_wal())
+	if (dlist_is_empty(&pgStatPending))
 	{
-		Assert(pending_since == 0);
-		return 0;
+		bool		do_flush = false;
+
+		/* Check for pending fixed-numbered stats */
+		for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
+		{
+			const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
+
+			if (!kind_info)
+				continue;
+			if (!kind_info->fixed_amount)
+			{
+				Assert(kind_info->have_fixed_pending_cb == NULL);
+				continue;
+			}
+			if (!kind_info->have_fixed_pending_cb)
+				continue;
+
+			if (kind_info->have_fixed_pending_cb())
+			{
+				do_flush = true;
+				break;
+			}
+		}
+
+		if (!do_flush)
+		{
+			Assert(pending_since == 0);
+			return 0;
+		}
 	}
 
 	/*
@@ -720,14 +750,23 @@ pgstat_report_stat(bool force)
 	/* flush database / relation / function / ... stats */
 	partial_flush |= pgstat_flush_pending_entries(nowait);
 
-	/* flush IO stats */
-	partial_flush |= pgstat_flush_io(nowait);
+	/* flush of fixed-numbered stats */
+	for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
+	{
+		const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
 
-	/* flush wal stats */
-	partial_flush |= pgstat_flush_wal(nowait);
+		if (!kind_info)
+			continue;
+		if (!kind_info->fixed_amount)
+		{
+			Assert(kind_info->flush_fixed_cb == NULL);
+			continue;
+		}
+		if (!kind_info->flush_fixed_cb)
+			continue;
 
-	/* flush SLRU stats */
-	partial_flush |= pgstat_slru_flush(nowait);
+		partial_flush |= kind_info->flush_fixed_cb(nowait);
+	}
 
 	last_flush = now;
 
diff --git a/src/backend/utils/activity/pgstat_io.c b/src/backend/utils/activity/pgstat_io.c
index 8af55989ee..cc2ffc78aa 100644
--- a/src/backend/utils/activity/pgstat_io.c
+++ b/src/backend/utils/activity/pgstat_io.c
@@ -29,7 +29,7 @@ typedef struct PgStat_PendingIO
 
 
 static PgStat_PendingIO PendingIOStats;
-bool		have_iostats = false;
+static bool have_iostats = false;
 
 
 /*
@@ -161,6 +161,24 @@ pgstat_fetch_stat_io(void)
 	return &pgStatLocal.snapshot.io;
 }
 
+/*
+ * Check if there any IO stats waiting for flush.
+ */
+bool
+pgstat_io_have_pending_cb(void)
+{
+	return have_iostats;
+}
+
+/*
+ * Simpler wrapper of pgstat_io_flush_cb()
+ */
+void
+pgstat_flush_io(bool nowait)
+{
+	(void) pgstat_io_flush_cb(nowait);
+}
+
 /*
  * Flush out locally pending IO statistics
  *
@@ -170,7 +188,7 @@ pgstat_fetch_stat_io(void)
  * acquired. Otherwise, return false.
  */
 bool
-pgstat_flush_io(bool nowait)
+pgstat_io_flush_cb(bool nowait)
 {
 	LWLock	   *bktype_lock;
 	PgStat_BktypeIO *bktype_shstats;
diff --git a/src/backend/utils/activity/pgstat_slru.c b/src/backend/utils/activity/pgstat_slru.c
index 6f922a85bf..dd6f9f840e 100644
--- a/src/backend/utils/activity/pgstat_slru.c
+++ b/src/backend/utils/activity/pgstat_slru.c
@@ -32,7 +32,7 @@ static void pgstat_reset_slru_counter_internal(int index, TimestampTz ts);
  * in order to avoid memory allocation.
  */
 static PgStat_SLRUStats pending_SLRUStats[SLRU_NUM_ELEMENTS];
-bool		have_slrustats = false;
+static bool have_slrustats = false;
 
 
 /*
@@ -143,6 +143,15 @@ pgstat_get_slru_index(const char *name)
 	return (SLRU_NUM_ELEMENTS - 1);
 }
 
+/*
+ * Check if there are any SLRU stats entries waiting for flush.
+ */
+bool
+pgstat_slru_have_pending_cb(void)
+{
+	return have_slrustats;
+}
+
 /*
  * Flush out locally pending SLRU stats entries
  *
@@ -153,7 +162,7 @@ pgstat_get_slru_index(const char *name)
  * acquired. Otherwise return false.
  */
 bool
-pgstat_slru_flush(bool nowait)
+pgstat_slru_flush_cb(bool nowait)
 {
 	PgStatShared_SLRU *stats_shmem = &pgStatLocal.shmem->slru;
 	int			i;
diff --git a/src/backend/utils/activity/pgstat_wal.c b/src/backend/utils/activity/pgstat_wal.c
index e2a3f6b865..3d76091f71 100644
--- a/src/backend/utils/activity/pgstat_wal.c
+++ b/src/backend/utils/activity/pgstat_wal.c
@@ -71,6 +71,15 @@ pgstat_fetch_stat_wal(void)
 	return &pgStatLocal.snapshot.wal;
 }
 
+/*
+ * Simple wrapper of pgstat_wal_flush_cb()
+ */
+void
+pgstat_flush_wal(bool nowait)
+{
+	(void) pgstat_wal_flush_cb(nowait);
+}
+
 /*
  * Calculate how much WAL usage counters have increased by subtracting the
  * previous counters from the current ones.
@@ -79,7 +88,7 @@ pgstat_fetch_stat_wal(void)
  * acquired. Otherwise return false.
  */
 bool
-pgstat_flush_wal(bool nowait)
+pgstat_wal_flush_cb(bool nowait)
 {
 	PgStatShared_Wal *stats_shmem = &pgStatLocal.shmem->wal;
 	WalUsage	wal_usage_diff = {0};
@@ -92,7 +101,7 @@ pgstat_flush_wal(bool nowait)
 	 * This function can be called even if nothing at all has happened. Avoid
 	 * taking lock for nothing in that case.
 	 */
-	if (!pgstat_have_pending_wal())
+	if (!pgstat_wal_have_pending_cb())
 		return false;
 
 	/*
@@ -141,8 +150,8 @@ void
 pgstat_init_wal(void)
 {
 	/*
-	 * Initialize prevWalUsage with pgWalUsage so that pgstat_flush_wal() can
-	 * calculate how much pgWalUsage counters are increased by subtracting
+	 * Initialize prevWalUsage with pgWalUsage so that pgstat_wal_flush_cb()
+	 * can calculate how much pgWalUsage counters are increased by subtracting
 	 * prevWalUsage from pgWalUsage.
 	 */
 	prevWalUsage = pgWalUsage;
@@ -156,7 +165,7 @@ pgstat_init_wal(void)
  * data pages.
  */
 bool
-pgstat_have_pending_wal(void)
+pgstat_wal_have_pending_cb(void)
 {
 	return pgWalUsage.wal_records != prevWalUsage.wal_records ||
 		PendingWalStats.wal_write != 0 ||
-- 
2.45.2

Attachment: signature.asc
Description: PGP signature

Reply via email to