From 596424c30af63e96f3ab4274541ab6cb6cfaab0e Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 3 Jul 2024 13:26:22 +0900
Subject: [PATCH v3 1/6] Rework handling of fixed-numbered statistics in
 pgstats

This commit removes fixed-numbered statistics structures from
PgStat_ShmemControl, the central shared memory structure of pgstats and
PgStat_Snapshot, in charge of holding stats snapshots for backends,
based on copies of what is in shared memory.

This has the advantage to simplify the read and write of the pgstats
file by not tracking the types of fixed-numbered stats in pgstat.c
itself, replacing it with what PgStat_KindInfo already knows for each
one of them.

This refactoring makes possible the introduction of more pluggable APIs
into pgstats for fixed-numbered stats.  The following changes are
applied to the internal structures:
- PgStat_ShmemControl holds an array of void* indexed by
PGSTAT_NUM_KINDS, pointing to shared memory areas allocated for each
fixed-numbered stats.  Each entry is allocated a size corresponding to
PgStat_KindInfo->shared_size.
- PgStat_Snapshot holds an array of void* also indexed by
PGSTAT_NUM_KINDS, pointing to the fixed stats stored in the snapshots.
These have a size of PgStat_KindInfo->shared_data_len.
- Fixed numbered stats now set shared_size, so as
- A callback is added to initialize the shared memory assigned to each
fixed-numbered stats, consisting of LWLock initializations for the
current types of stats.  So this initialization is moved out of pgstat.c
into each stats kind file.
---
 src/include/utils/pgstat_internal.h           |  52 +++----
 src/backend/utils/activity/pgstat.c           | 130 ++++++++++--------
 src/backend/utils/activity/pgstat_archiver.c  |  23 +++-
 src/backend/utils/activity/pgstat_bgwriter.c  |  26 +++-
 .../utils/activity/pgstat_checkpointer.c      |  26 +++-
 src/backend/utils/activity/pgstat_io.c        |  44 ++++--
 src/backend/utils/activity/pgstat_shmem.c     |  33 +++--
 src/backend/utils/activity/pgstat_slru.c      |  25 +++-
 src/backend/utils/activity/pgstat_wal.c       |  26 +++-
 9 files changed, 250 insertions(+), 135 deletions(-)

diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index e21ef4e2c9..b8b2152d71 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -195,16 +195,11 @@ typedef struct PgStat_KindInfo
 
 	/*
 	 * The size of an entry in the shared stats hash table (pointed to by
-	 * PgStatShared_HashEntry->body).
+	 * PgStatShared_HashEntry->body).  For fixed-numbered statistics, this is
+	 * the size of an entry in PgStat_ShmemControl->fixed_data.
 	 */
 	uint32		shared_size;
 
-	/*
-	 * The offset of the statistics struct in the containing shared memory
-	 * control structure PgStat_ShmemControl, for fixed-numbered statistics.
-	 */
-	uint32		shared_ctl_off;
-
 	/*
 	 * The offset/size of statistics inside the shared stats entry. Used when
 	 * [de-]serializing statistics to / from disk respectively. Separate from
@@ -245,6 +240,13 @@ typedef struct PgStat_KindInfo
 									   const PgStatShared_Common *header, NameData *name);
 	bool		(*from_serialized_name) (const NameData *name, PgStat_HashKey *key);
 
+	/*
+	 * For fixed-numbered statistics: Initialize shared memory state.
+	 *
+	 * "stats" is the pointer to the allocated shared memory area.
+	 */
+	void		(*init_shmem_cb) (void *stats);
+
 	/*
 	 * For fixed-numbered statistics: Reset All.
 	 */
@@ -425,14 +427,12 @@ typedef struct PgStat_ShmemControl
 	pg_atomic_uint64 gc_request_count;
 
 	/*
-	 * Stats data for fixed-numbered objects.
+	 * Stats data for fixed-numbered objects, indexed by PgStat_Kind.
+	 *
+	 * Each entry has a size of PgStat_KindInfo->shared_size.
 	 */
-	PgStatShared_Archiver archiver;
-	PgStatShared_BgWriter bgwriter;
-	PgStatShared_Checkpointer checkpointer;
-	PgStatShared_IO io;
-	PgStatShared_SLRU slru;
-	PgStatShared_Wal wal;
+	void	   *fixed_data[PGSTAT_NUM_KINDS];
+
 } PgStat_ShmemControl;
 
 
@@ -446,19 +446,13 @@ typedef struct PgStat_Snapshot
 	/* time at which snapshot was taken */
 	TimestampTz snapshot_timestamp;
 
+	/*
+	 * Data in snapshot for fixed-numbered statistics, indexed by PgStat_Kind.
+	 * Each entry is allocated in TopMemoryContext, for a size of
+	 * shared_data_len.
+	 */
 	bool		fixed_valid[PGSTAT_NUM_KINDS];
-
-	PgStat_ArchiverStats archiver;
-
-	PgStat_BgWriterStats bgwriter;
-
-	PgStat_CheckpointerStats checkpointer;
-
-	PgStat_IO	io;
-
-	PgStat_SLRUStats slru[SLRU_NUM_ELEMENTS];
-
-	PgStat_WalStats wal;
+	void	   *fixed_data[PGSTAT_NUM_KINDS];
 
 	/* to free snapshot in bulk */
 	MemoryContext context;
@@ -522,6 +516,7 @@ extern void pgstat_snapshot_fixed(PgStat_Kind kind);
  * Functions in pgstat_archiver.c
  */
 
+extern void pgstat_archiver_init_shmem_cb(void *stats);
 extern void pgstat_archiver_reset_all_cb(TimestampTz ts);
 extern void pgstat_archiver_snapshot_cb(void);
 
@@ -530,6 +525,7 @@ extern void pgstat_archiver_snapshot_cb(void);
  * Functions in pgstat_bgwriter.c
  */
 
+extern void pgstat_bgwriter_init_shmem_cb(void *stats);
 extern void pgstat_bgwriter_reset_all_cb(TimestampTz ts);
 extern void pgstat_bgwriter_snapshot_cb(void);
 
@@ -538,6 +534,7 @@ extern void pgstat_bgwriter_snapshot_cb(void);
  * Functions in pgstat_checkpointer.c
  */
 
+extern void pgstat_checkpointer_init_shmem_cb(void *stats);
 extern void pgstat_checkpointer_reset_all_cb(TimestampTz ts);
 extern void pgstat_checkpointer_snapshot_cb(void);
 
@@ -568,6 +565,7 @@ extern bool pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
  */
 
 extern bool pgstat_flush_io(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);
 
@@ -626,6 +624,7 @@ extern PgStatShared_Common *pgstat_init_entry(PgStat_Kind kind,
  */
 
 extern bool pgstat_slru_flush(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);
 
@@ -638,6 +637,7 @@ extern bool pgstat_flush_wal(bool nowait);
 extern void pgstat_init_wal(void);
 extern bool pgstat_have_pending_wal(void);
 
+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);
 
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index c37c11b2ec..13ddbcdcfb 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -132,6 +132,7 @@
  * ---------
  */
 #define PGSTAT_FILE_ENTRY_END	'E' /* end of file */
+#define PGSTAT_FILE_ENTRY_FIXED	'F' /* fixed-numbered stats entry */
 #define PGSTAT_FILE_ENTRY_NAME	'N' /* stats entry identified by name */
 #define PGSTAT_FILE_ENTRY_HASH	'S' /* stats entry identified by
 									 * PgStat_HashKey */
@@ -173,6 +174,8 @@ typedef struct PgStat_SnapshotEntry
 static void pgstat_write_statsfile(void);
 static void pgstat_read_statsfile(void);
 
+static void pgstat_init_snapshot(void);
+
 static void pgstat_reset_after_failure(void);
 
 static bool pgstat_flush_pending_entries(bool nowait);
@@ -349,10 +352,11 @@ static const PgStat_KindInfo pgstat_kind_infos[PGSTAT_NUM_KINDS] = {
 
 		.fixed_amount = true,
 
-		.shared_ctl_off = offsetof(PgStat_ShmemControl, archiver),
+		.shared_size = sizeof(PgStatShared_Archiver),
 		.shared_data_off = offsetof(PgStatShared_Archiver, stats),
 		.shared_data_len = sizeof(((PgStatShared_Archiver *) 0)->stats),
 
+		.init_shmem_cb = pgstat_archiver_init_shmem_cb,
 		.reset_all_cb = pgstat_archiver_reset_all_cb,
 		.snapshot_cb = pgstat_archiver_snapshot_cb,
 	},
@@ -362,10 +366,11 @@ static const PgStat_KindInfo pgstat_kind_infos[PGSTAT_NUM_KINDS] = {
 
 		.fixed_amount = true,
 
-		.shared_ctl_off = offsetof(PgStat_ShmemControl, bgwriter),
+		.shared_size = sizeof(PgStatShared_BgWriter),
 		.shared_data_off = offsetof(PgStatShared_BgWriter, stats),
 		.shared_data_len = sizeof(((PgStatShared_BgWriter *) 0)->stats),
 
+		.init_shmem_cb = pgstat_bgwriter_init_shmem_cb,
 		.reset_all_cb = pgstat_bgwriter_reset_all_cb,
 		.snapshot_cb = pgstat_bgwriter_snapshot_cb,
 	},
@@ -375,10 +380,11 @@ static const PgStat_KindInfo pgstat_kind_infos[PGSTAT_NUM_KINDS] = {
 
 		.fixed_amount = true,
 
-		.shared_ctl_off = offsetof(PgStat_ShmemControl, checkpointer),
+		.shared_size = sizeof(PgStatShared_Checkpointer),
 		.shared_data_off = offsetof(PgStatShared_Checkpointer, stats),
 		.shared_data_len = sizeof(((PgStatShared_Checkpointer *) 0)->stats),
 
+		.init_shmem_cb = pgstat_checkpointer_init_shmem_cb,
 		.reset_all_cb = pgstat_checkpointer_reset_all_cb,
 		.snapshot_cb = pgstat_checkpointer_snapshot_cb,
 	},
@@ -388,10 +394,11 @@ static const PgStat_KindInfo pgstat_kind_infos[PGSTAT_NUM_KINDS] = {
 
 		.fixed_amount = true,
 
-		.shared_ctl_off = offsetof(PgStat_ShmemControl, io),
+		.shared_size = sizeof(PgStatShared_IO),
 		.shared_data_off = offsetof(PgStatShared_IO, stats),
 		.shared_data_len = sizeof(((PgStatShared_IO *) 0)->stats),
 
+		.init_shmem_cb = pgstat_io_init_shmem_cb,
 		.reset_all_cb = pgstat_io_reset_all_cb,
 		.snapshot_cb = pgstat_io_snapshot_cb,
 	},
@@ -401,10 +408,11 @@ static const PgStat_KindInfo pgstat_kind_infos[PGSTAT_NUM_KINDS] = {
 
 		.fixed_amount = true,
 
-		.shared_ctl_off = offsetof(PgStat_ShmemControl, slru),
+		.shared_size = sizeof(PgStatShared_SLRU),
 		.shared_data_off = offsetof(PgStatShared_SLRU, stats),
 		.shared_data_len = sizeof(((PgStatShared_SLRU *) 0)->stats),
 
+		.init_shmem_cb = pgstat_slru_init_shmem_cb,
 		.reset_all_cb = pgstat_slru_reset_all_cb,
 		.snapshot_cb = pgstat_slru_snapshot_cb,
 	},
@@ -414,10 +422,11 @@ static const PgStat_KindInfo pgstat_kind_infos[PGSTAT_NUM_KINDS] = {
 
 		.fixed_amount = true,
 
-		.shared_ctl_off = offsetof(PgStat_ShmemControl, wal),
+		.shared_size = sizeof(PgStatShared_Wal),
 		.shared_data_off = offsetof(PgStatShared_Wal, stats),
 		.shared_data_len = sizeof(((PgStatShared_Wal *) 0)->stats),
 
+		.init_shmem_cb = pgstat_wal_init_shmem_cb,
 		.reset_all_cb = pgstat_wal_reset_all_cb,
 		.snapshot_cb = pgstat_wal_snapshot_cb,
 	},
@@ -571,6 +580,8 @@ pgstat_initialize(void)
 
 	pgstat_attach_shmem();
 
+	pgstat_init_snapshot();
+
 	pgstat_init_wal();
 
 	/* Set up a process-exit hook to clean up */
@@ -982,6 +993,22 @@ pgstat_snapshot_fixed(PgStat_Kind kind)
 	Assert(pgStatLocal.snapshot.fixed_valid[kind]);
 }
 
+static void
+pgstat_init_snapshot(void)
+{
+	/* Initialize fixed-numbered statistics data in snapshots */
+	for (int kind = PGSTAT_KIND_FIRST_VALID; kind <= PGSTAT_KIND_LAST; kind++)
+	{
+		const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
+
+		if (!kind_info->fixed_amount)
+			continue;
+
+		pgStatLocal.snapshot.fixed_data[kind] =
+			MemoryContextAlloc(TopMemoryContext, kind_info->shared_data_len);
+	}
+}
+
 static void
 pgstat_prep_snapshot(void)
 {
@@ -1371,47 +1398,25 @@ pgstat_write_statsfile(void)
 	format_id = PGSTAT_FILE_FORMAT_ID;
 	write_chunk_s(fpout, &format_id);
 
-	/*
-	 * XXX: The following could now be generalized to just iterate over
-	 * pgstat_kind_infos instead of knowing about the different kinds of
-	 * stats.
-	 */
+	/* Write various stats structs with fixed number of objects */
+	for (int kind = PGSTAT_KIND_FIRST_VALID; kind <= PGSTAT_KIND_LAST; kind++)
+	{
+		char	   *ptr;
+		const PgStat_KindInfo *info = pgstat_get_kind_info(kind);
 
-	/*
-	 * Write archiver stats struct
-	 */
-	pgstat_build_snapshot_fixed(PGSTAT_KIND_ARCHIVER);
-	write_chunk_s(fpout, &pgStatLocal.snapshot.archiver);
+		if (!info->fixed_amount)
+			continue;
 
-	/*
-	 * Write bgwriter stats struct
-	 */
-	pgstat_build_snapshot_fixed(PGSTAT_KIND_BGWRITER);
-	write_chunk_s(fpout, &pgStatLocal.snapshot.bgwriter);
+		Assert(info->shared_size != 0 && info->shared_data_len != 0);
 
-	/*
-	 * Write checkpointer stats struct
-	 */
-	pgstat_build_snapshot_fixed(PGSTAT_KIND_CHECKPOINTER);
-	write_chunk_s(fpout, &pgStatLocal.snapshot.checkpointer);
+		/* prepare snapshot data and write it */
+		pgstat_build_snapshot_fixed(kind);
+		ptr = pgStatLocal.snapshot.fixed_data[kind];
 
-	/*
-	 * Write IO stats struct
-	 */
-	pgstat_build_snapshot_fixed(PGSTAT_KIND_IO);
-	write_chunk_s(fpout, &pgStatLocal.snapshot.io);
-
-	/*
-	 * Write SLRU stats struct
-	 */
-	pgstat_build_snapshot_fixed(PGSTAT_KIND_SLRU);
-	write_chunk_s(fpout, &pgStatLocal.snapshot.slru);
-
-	/*
-	 * Write WAL stats struct
-	 */
-	pgstat_build_snapshot_fixed(PGSTAT_KIND_WAL);
-	write_chunk_s(fpout, &pgStatLocal.snapshot.wal);
+		fputc(PGSTAT_FILE_ENTRY_FIXED, fpout);
+		write_chunk_s(fpout, &kind);
+		write_chunk(fpout, ptr, info->shared_data_len);
+	}
 
 	/*
 	 * Walk through the stats entries
@@ -1551,22 +1556,6 @@ pgstat_read_statsfile(void)
 		format_id != PGSTAT_FILE_FORMAT_ID)
 		goto error;
 
-	/* Read various stats structs with fixed number of objects */
-	for (int kind = PGSTAT_KIND_FIRST_VALID; kind <= PGSTAT_KIND_LAST; kind++)
-	{
-		char	   *ptr;
-		const PgStat_KindInfo *info = pgstat_get_kind_info(kind);
-
-		if (!info->fixed_amount)
-			continue;
-
-		Assert(info->shared_ctl_off != 0);
-
-		ptr = ((char *) shmem) + info->shared_ctl_off + info->shared_data_off;
-		if (!read_chunk(fpin, ptr, info->shared_data_len))
-			goto error;
-	}
-
 	/*
 	 * We found an existing statistics file. Read it and put all the hash
 	 * table entries into place.
@@ -1577,6 +1566,29 @@ pgstat_read_statsfile(void)
 
 		switch (t)
 		{
+			case PGSTAT_FILE_ENTRY_FIXED:
+				{
+					PgStat_Kind kind;
+					const PgStat_KindInfo *info;
+					char	   *ptr;
+
+					if (!read_chunk_s(fpin, &kind))
+						goto error;
+
+					if (!pgstat_is_kind_valid(kind))
+						goto error;
+
+					info = pgstat_get_kind_info(kind);
+					Assert(info->fixed_amount);
+
+					/* Load back stats into shared memory */
+					ptr = ((char *) shmem->fixed_data[kind]) + info->shared_data_off;
+					if (!read_chunk(fpin, ptr,
+									info->shared_data_len))
+						goto error;
+
+					break;
+				}
 			case PGSTAT_FILE_ENTRY_HASH:
 			case PGSTAT_FILE_ENTRY_NAME:
 				{
diff --git a/src/backend/utils/activity/pgstat_archiver.c b/src/backend/utils/activity/pgstat_archiver.c
index 66398b20e5..99cd461f81 100644
--- a/src/backend/utils/activity/pgstat_archiver.c
+++ b/src/backend/utils/activity/pgstat_archiver.c
@@ -27,7 +27,8 @@
 void
 pgstat_report_archiver(const char *xlog, bool failed)
 {
-	PgStatShared_Archiver *stats_shmem = &pgStatLocal.shmem->archiver;
+	PgStatShared_Archiver *stats_shmem = (PgStatShared_Archiver *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_ARCHIVER];
 	TimestampTz now = GetCurrentTimestamp();
 
 	pgstat_begin_changecount_write(&stats_shmem->changecount);
@@ -59,13 +60,23 @@ pgstat_fetch_stat_archiver(void)
 {
 	pgstat_snapshot_fixed(PGSTAT_KIND_ARCHIVER);
 
-	return &pgStatLocal.snapshot.archiver;
+	return (PgStat_ArchiverStats *)
+		pgStatLocal.snapshot.fixed_data[PGSTAT_KIND_ARCHIVER];
+}
+
+void
+pgstat_archiver_init_shmem_cb(void *stats)
+{
+	PgStatShared_Archiver *stats_shmem = (PgStatShared_Archiver *) stats;
+
+	LWLockInitialize(&stats_shmem->lock, LWTRANCHE_PGSTATS_DATA);
 }
 
 void
 pgstat_archiver_reset_all_cb(TimestampTz ts)
 {
-	PgStatShared_Archiver *stats_shmem = &pgStatLocal.shmem->archiver;
+	PgStatShared_Archiver *stats_shmem = (PgStatShared_Archiver *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_ARCHIVER];
 
 	/* see explanation above PgStatShared_Archiver for the reset protocol */
 	LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE);
@@ -80,8 +91,10 @@ pgstat_archiver_reset_all_cb(TimestampTz ts)
 void
 pgstat_archiver_snapshot_cb(void)
 {
-	PgStatShared_Archiver *stats_shmem = &pgStatLocal.shmem->archiver;
-	PgStat_ArchiverStats *stat_snap = &pgStatLocal.snapshot.archiver;
+	PgStatShared_Archiver *stats_shmem = (PgStatShared_Archiver *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_ARCHIVER];
+	PgStat_ArchiverStats *stat_snap = (PgStat_ArchiverStats *)
+		pgStatLocal.snapshot.fixed_data[PGSTAT_KIND_ARCHIVER];
 	PgStat_ArchiverStats *reset_offset = &stats_shmem->reset_offset;
 	PgStat_ArchiverStats reset;
 
diff --git a/src/backend/utils/activity/pgstat_bgwriter.c b/src/backend/utils/activity/pgstat_bgwriter.c
index 7d2432e4fa..778d3f0b0e 100644
--- a/src/backend/utils/activity/pgstat_bgwriter.c
+++ b/src/backend/utils/activity/pgstat_bgwriter.c
@@ -29,7 +29,8 @@ PgStat_BgWriterStats PendingBgWriterStats = {0};
 void
 pgstat_report_bgwriter(void)
 {
-	PgStatShared_BgWriter *stats_shmem = &pgStatLocal.shmem->bgwriter;
+	PgStatShared_BgWriter *stats_shmem = (PgStatShared_BgWriter *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_BGWRITER];
 	static const PgStat_BgWriterStats all_zeroes;
 
 	Assert(!pgStatLocal.shmem->is_shutdown);
@@ -72,13 +73,23 @@ pgstat_fetch_stat_bgwriter(void)
 {
 	pgstat_snapshot_fixed(PGSTAT_KIND_BGWRITER);
 
-	return &pgStatLocal.snapshot.bgwriter;
+	return (PgStat_BgWriterStats *)
+		pgStatLocal.snapshot.fixed_data[PGSTAT_KIND_BGWRITER];
+}
+
+void
+pgstat_bgwriter_init_shmem_cb(void *stats)
+{
+	PgStatShared_BgWriter *stats_shmem = (PgStatShared_BgWriter *) stats;
+
+	LWLockInitialize(&stats_shmem->lock, LWTRANCHE_PGSTATS_DATA);
 }
 
 void
 pgstat_bgwriter_reset_all_cb(TimestampTz ts)
 {
-	PgStatShared_BgWriter *stats_shmem = &pgStatLocal.shmem->bgwriter;
+	PgStatShared_BgWriter *stats_shmem = (PgStatShared_BgWriter *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_BGWRITER];
 
 	/* see explanation above PgStatShared_BgWriter for the reset protocol */
 	LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE);
@@ -93,11 +104,14 @@ pgstat_bgwriter_reset_all_cb(TimestampTz ts)
 void
 pgstat_bgwriter_snapshot_cb(void)
 {
-	PgStatShared_BgWriter *stats_shmem = &pgStatLocal.shmem->bgwriter;
+	PgStatShared_BgWriter *stats_shmem = (PgStatShared_BgWriter *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_BGWRITER];
+	PgStat_BgWriterStats *stat_snap = (PgStat_BgWriterStats *)
+		pgStatLocal.snapshot.fixed_data[PGSTAT_KIND_BGWRITER];
 	PgStat_BgWriterStats *reset_offset = &stats_shmem->reset_offset;
 	PgStat_BgWriterStats reset;
 
-	pgstat_copy_changecounted_stats(&pgStatLocal.snapshot.bgwriter,
+	pgstat_copy_changecounted_stats(stat_snap,
 									&stats_shmem->stats,
 									sizeof(stats_shmem->stats),
 									&stats_shmem->changecount);
@@ -107,7 +121,7 @@ pgstat_bgwriter_snapshot_cb(void)
 	LWLockRelease(&stats_shmem->lock);
 
 	/* compensate by reset offsets */
-#define BGWRITER_COMP(fld) pgStatLocal.snapshot.bgwriter.fld -= reset.fld;
+#define BGWRITER_COMP(fld) stat_snap->fld -= reset.fld;
 	BGWRITER_COMP(buf_written_clean);
 	BGWRITER_COMP(maxwritten_clean);
 	BGWRITER_COMP(buf_alloc);
diff --git a/src/backend/utils/activity/pgstat_checkpointer.c b/src/backend/utils/activity/pgstat_checkpointer.c
index 30a8110e38..6e86255b8d 100644
--- a/src/backend/utils/activity/pgstat_checkpointer.c
+++ b/src/backend/utils/activity/pgstat_checkpointer.c
@@ -31,7 +31,8 @@ pgstat_report_checkpointer(void)
 {
 	/* We assume this initializes to zeroes */
 	static const PgStat_CheckpointerStats all_zeroes;
-	PgStatShared_Checkpointer *stats_shmem = &pgStatLocal.shmem->checkpointer;
+	PgStatShared_Checkpointer *stats_shmem = (PgStatShared_Checkpointer *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_CHECKPOINTER];
 
 	Assert(!pgStatLocal.shmem->is_shutdown);
 	pgstat_assert_is_up();
@@ -81,13 +82,23 @@ pgstat_fetch_stat_checkpointer(void)
 {
 	pgstat_snapshot_fixed(PGSTAT_KIND_CHECKPOINTER);
 
-	return &pgStatLocal.snapshot.checkpointer;
+	return (PgStat_CheckpointerStats *)
+		pgStatLocal.snapshot.fixed_data[PGSTAT_KIND_CHECKPOINTER];
+}
+
+void
+pgstat_checkpointer_init_shmem_cb(void *stats)
+{
+	PgStatShared_Checkpointer *stats_shmem = (PgStatShared_Checkpointer *) stats;
+
+	LWLockInitialize(&stats_shmem->lock, LWTRANCHE_PGSTATS_DATA);
 }
 
 void
 pgstat_checkpointer_reset_all_cb(TimestampTz ts)
 {
-	PgStatShared_Checkpointer *stats_shmem = &pgStatLocal.shmem->checkpointer;
+	PgStatShared_Checkpointer *stats_shmem = (PgStatShared_Checkpointer *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_CHECKPOINTER];
 
 	/* see explanation above PgStatShared_Checkpointer for the reset protocol */
 	LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE);
@@ -102,11 +113,14 @@ pgstat_checkpointer_reset_all_cb(TimestampTz ts)
 void
 pgstat_checkpointer_snapshot_cb(void)
 {
-	PgStatShared_Checkpointer *stats_shmem = &pgStatLocal.shmem->checkpointer;
+	PgStatShared_Checkpointer *stats_shmem = (PgStatShared_Checkpointer *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_CHECKPOINTER];
+	PgStat_CheckpointerStats *stat_snap = (PgStat_CheckpointerStats *)
+		pgStatLocal.snapshot.fixed_data[PGSTAT_KIND_CHECKPOINTER];
 	PgStat_CheckpointerStats *reset_offset = &stats_shmem->reset_offset;
 	PgStat_CheckpointerStats reset;
 
-	pgstat_copy_changecounted_stats(&pgStatLocal.snapshot.checkpointer,
+	pgstat_copy_changecounted_stats(stat_snap,
 									&stats_shmem->stats,
 									sizeof(stats_shmem->stats),
 									&stats_shmem->changecount);
@@ -116,7 +130,7 @@ pgstat_checkpointer_snapshot_cb(void)
 	LWLockRelease(&stats_shmem->lock);
 
 	/* compensate by reset offsets */
-#define CHECKPOINTER_COMP(fld) pgStatLocal.snapshot.checkpointer.fld -= reset.fld;
+#define CHECKPOINTER_COMP(fld) stat_snap->fld -= reset.fld;
 	CHECKPOINTER_COMP(num_timed);
 	CHECKPOINTER_COMP(num_requested);
 	CHECKPOINTER_COMP(restartpoints_timed);
diff --git a/src/backend/utils/activity/pgstat_io.c b/src/backend/utils/activity/pgstat_io.c
index 9d6e067382..6cbbc2094a 100644
--- a/src/backend/utils/activity/pgstat_io.c
+++ b/src/backend/utils/activity/pgstat_io.c
@@ -158,7 +158,8 @@ pgstat_fetch_stat_io(void)
 {
 	pgstat_snapshot_fixed(PGSTAT_KIND_IO);
 
-	return &pgStatLocal.snapshot.io;
+	return (PgStat_IO *)
+		pgStatLocal.snapshot.fixed_data[PGSTAT_KIND_IO];
 }
 
 /*
@@ -174,13 +175,16 @@ pgstat_flush_io(bool nowait)
 {
 	LWLock	   *bktype_lock;
 	PgStat_BktypeIO *bktype_shstats;
+	PgStatShared_IO *stat_shmem;
 
 	if (!have_iostats)
 		return false;
 
-	bktype_lock = &pgStatLocal.shmem->io.locks[MyBackendType];
-	bktype_shstats =
-		&pgStatLocal.shmem->io.stats.stats[MyBackendType];
+	stat_shmem = (PgStatShared_IO *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_IO];
+
+	bktype_lock = &stat_shmem->locks[MyBackendType];
+	bktype_shstats = &stat_shmem->stats.stats[MyBackendType];
 
 	if (!nowait)
 		LWLockAcquire(bktype_lock, LW_EXCLUSIVE);
@@ -251,13 +255,25 @@ pgstat_get_io_object_name(IOObject io_object)
 	pg_unreachable();
 }
 
+void
+pgstat_io_init_shmem_cb(void *stats)
+{
+	PgStatShared_IO *stat_shmem = (PgStatShared_IO *) stats;
+
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+		LWLockInitialize(&stat_shmem->locks[i], LWTRANCHE_PGSTATS_DATA);
+}
+
 void
 pgstat_io_reset_all_cb(TimestampTz ts)
 {
+	PgStatShared_IO *stat_shmem = (PgStatShared_IO *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_IO];
+
 	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
 	{
-		LWLock	   *bktype_lock = &pgStatLocal.shmem->io.locks[i];
-		PgStat_BktypeIO *bktype_shstats = &pgStatLocal.shmem->io.stats.stats[i];
+		LWLock	   *bktype_lock = &stat_shmem->locks[i];
+		PgStat_BktypeIO *bktype_shstats = &stat_shmem->stats.stats[i];
 
 		LWLockAcquire(bktype_lock, LW_EXCLUSIVE);
 
@@ -266,7 +282,7 @@ pgstat_io_reset_all_cb(TimestampTz ts)
 		 * the reset timestamp as well.
 		 */
 		if (i == 0)
-			pgStatLocal.shmem->io.stats.stat_reset_timestamp = ts;
+			stat_shmem->stats.stat_reset_timestamp = ts;
 
 		memset(bktype_shstats, 0, sizeof(*bktype_shstats));
 		LWLockRelease(bktype_lock);
@@ -276,11 +292,16 @@ pgstat_io_reset_all_cb(TimestampTz ts)
 void
 pgstat_io_snapshot_cb(void)
 {
+	PgStatShared_IO *stat_shmem = (PgStatShared_IO *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_IO];
+	PgStat_IO  *stat_snap = (PgStat_IO *)
+		pgStatLocal.snapshot.fixed_data[PGSTAT_KIND_IO];
+
 	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
 	{
-		LWLock	   *bktype_lock = &pgStatLocal.shmem->io.locks[i];
-		PgStat_BktypeIO *bktype_shstats = &pgStatLocal.shmem->io.stats.stats[i];
-		PgStat_BktypeIO *bktype_snap = &pgStatLocal.snapshot.io.stats[i];
+		LWLock	   *bktype_lock = &stat_shmem->locks[i];
+		PgStat_BktypeIO *bktype_shstats = &stat_shmem->stats.stats[i];
+		PgStat_BktypeIO *bktype_snap = &stat_snap->stats[i];
 
 		LWLockAcquire(bktype_lock, LW_SHARED);
 
@@ -289,8 +310,7 @@ pgstat_io_snapshot_cb(void)
 		 * the reset timestamp as well.
 		 */
 		if (i == 0)
-			pgStatLocal.snapshot.io.stat_reset_timestamp =
-				pgStatLocal.shmem->io.stats.stat_reset_timestamp;
+			stat_snap->stat_reset_timestamp = stat_shmem->stats.stat_reset_timestamp;
 
 		/* using struct assignment due to better type safety */
 		*bktype_snap = *bktype_shstats;
diff --git a/src/backend/utils/activity/pgstat_shmem.c b/src/backend/utils/activity/pgstat_shmem.c
index 634b967820..bd62c4b72a 100644
--- a/src/backend/utils/activity/pgstat_shmem.c
+++ b/src/backend/utils/activity/pgstat_shmem.c
@@ -131,6 +131,19 @@ StatsShmemSize(void)
 	sz = MAXALIGN(sizeof(PgStat_ShmemControl));
 	sz = add_size(sz, pgstat_dsa_init_size());
 
+	/* Add shared memory for all the fixed-numbered statistics */
+	for (int kind = PGSTAT_KIND_FIRST_VALID; kind <= PGSTAT_KIND_LAST; kind++)
+	{
+		const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
+
+		if (!kind_info->fixed_amount)
+			continue;
+
+		Assert(kind_info->shared_size != 0);
+
+		sz += MAXALIGN(kind_info->shared_size);
+	}
+
 	return sz;
 }
 
@@ -196,17 +209,19 @@ StatsShmemInit(void)
 
 		pg_atomic_init_u64(&ctl->gc_request_count, 1);
 
+		/* initialize fixed-numbered statistics */
+		for (int kind = PGSTAT_KIND_FIRST_VALID; kind <= PGSTAT_KIND_LAST; kind++)
+		{
+			const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
 
-		/* initialize fixed-numbered stats */
-		LWLockInitialize(&ctl->archiver.lock, LWTRANCHE_PGSTATS_DATA);
-		LWLockInitialize(&ctl->bgwriter.lock, LWTRANCHE_PGSTATS_DATA);
-		LWLockInitialize(&ctl->checkpointer.lock, LWTRANCHE_PGSTATS_DATA);
-		LWLockInitialize(&ctl->slru.lock, LWTRANCHE_PGSTATS_DATA);
-		LWLockInitialize(&ctl->wal.lock, LWTRANCHE_PGSTATS_DATA);
+			if (!kind_info->fixed_amount)
+				continue;
 
-		for (int i = 0; i < BACKEND_NUM_TYPES; i++)
-			LWLockInitialize(&ctl->io.locks[i],
-							 LWTRANCHE_PGSTATS_DATA);
+			Assert(kind_info->shared_size != 0);
+
+			ctl->fixed_data[kind] = ShmemAlloc(kind_info->shared_size);
+			kind_info->init_shmem_cb(ctl->fixed_data[kind]);
+		}
 	}
 	else
 	{
diff --git a/src/backend/utils/activity/pgstat_slru.c b/src/backend/utils/activity/pgstat_slru.c
index 56ea1c3378..d50589c1de 100644
--- a/src/backend/utils/activity/pgstat_slru.c
+++ b/src/backend/utils/activity/pgstat_slru.c
@@ -106,7 +106,8 @@ pgstat_fetch_slru(void)
 {
 	pgstat_snapshot_fixed(PGSTAT_KIND_SLRU);
 
-	return pgStatLocal.snapshot.slru;
+	return (PgStat_SLRUStats *)
+		pgStatLocal.snapshot.fixed_data[PGSTAT_KIND_SLRU];
 }
 
 /*
@@ -155,7 +156,8 @@ pgstat_get_slru_index(const char *name)
 bool
 pgstat_slru_flush(bool nowait)
 {
-	PgStatShared_SLRU *stats_shmem = &pgStatLocal.shmem->slru;
+	PgStatShared_SLRU *stats_shmem = (PgStatShared_SLRU *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_SLRU];
 	int			i;
 
 	if (!have_slrustats)
@@ -192,6 +194,14 @@ pgstat_slru_flush(bool nowait)
 	return false;
 }
 
+void
+pgstat_slru_init_shmem_cb(void *stats)
+{
+	PgStatShared_SLRU *stats_shmem = (PgStatShared_SLRU *) stats;
+
+	LWLockInitialize(&stats_shmem->lock, LWTRANCHE_PGSTATS_DATA);
+}
+
 void
 pgstat_slru_reset_all_cb(TimestampTz ts)
 {
@@ -202,12 +212,14 @@ pgstat_slru_reset_all_cb(TimestampTz ts)
 void
 pgstat_slru_snapshot_cb(void)
 {
-	PgStatShared_SLRU *stats_shmem = &pgStatLocal.shmem->slru;
+	PgStatShared_SLRU *stats_shmem = (PgStatShared_SLRU *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_SLRU];
+	PgStat_SLRUStats *stat_snap = (PgStat_SLRUStats *)
+		pgStatLocal.snapshot.fixed_data[PGSTAT_KIND_SLRU];
 
 	LWLockAcquire(&stats_shmem->lock, LW_SHARED);
 
-	memcpy(pgStatLocal.snapshot.slru, &stats_shmem->stats,
-		   sizeof(stats_shmem->stats));
+	memcpy(stat_snap, &stats_shmem->stats, sizeof(stats_shmem->stats));
 
 	LWLockRelease(&stats_shmem->lock);
 }
@@ -237,7 +249,8 @@ get_slru_entry(int slru_idx)
 static void
 pgstat_reset_slru_counter_internal(int index, TimestampTz ts)
 {
-	PgStatShared_SLRU *stats_shmem = &pgStatLocal.shmem->slru;
+	PgStatShared_SLRU *stats_shmem = (PgStatShared_SLRU *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_SLRU];
 
 	LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE);
 
diff --git a/src/backend/utils/activity/pgstat_wal.c b/src/backend/utils/activity/pgstat_wal.c
index 0e374f133a..030b339e47 100644
--- a/src/backend/utils/activity/pgstat_wal.c
+++ b/src/backend/utils/activity/pgstat_wal.c
@@ -68,7 +68,8 @@ pgstat_fetch_stat_wal(void)
 {
 	pgstat_snapshot_fixed(PGSTAT_KIND_WAL);
 
-	return &pgStatLocal.snapshot.wal;
+	return (PgStat_WalStats *)
+		pgStatLocal.snapshot.fixed_data[PGSTAT_KIND_WAL];
 }
 
 /*
@@ -81,7 +82,8 @@ pgstat_fetch_stat_wal(void)
 bool
 pgstat_flush_wal(bool nowait)
 {
-	PgStatShared_Wal *stats_shmem = &pgStatLocal.shmem->wal;
+	PgStatShared_Wal *stats_shmem = (PgStatShared_Wal *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_WAL];
 	WalUsage	wal_usage_diff = {0};
 
 	Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
@@ -163,10 +165,20 @@ pgstat_have_pending_wal(void)
 		PendingWalStats.wal_sync != 0;
 }
 
+void
+pgstat_wal_init_shmem_cb(void *stats)
+{
+	PgStatShared_Wal *stats_shmem = (PgStatShared_Wal *) stats;
+
+	LWLockInitialize(&stats_shmem->lock, LWTRANCHE_PGSTATS_DATA);
+}
+
+
 void
 pgstat_wal_reset_all_cb(TimestampTz ts)
 {
-	PgStatShared_Wal *stats_shmem = &pgStatLocal.shmem->wal;
+	PgStatShared_Wal *stats_shmem = (PgStatShared_Wal *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_WAL];
 
 	LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE);
 	memset(&stats_shmem->stats, 0, sizeof(stats_shmem->stats));
@@ -177,10 +189,12 @@ pgstat_wal_reset_all_cb(TimestampTz ts)
 void
 pgstat_wal_snapshot_cb(void)
 {
-	PgStatShared_Wal *stats_shmem = &pgStatLocal.shmem->wal;
+	PgStatShared_Wal *stats_shmem = (PgStatShared_Wal *)
+		pgStatLocal.shmem->fixed_data[PGSTAT_KIND_WAL];
+	PgStat_WalStats *stat_snap = (PgStat_WalStats *)
+		pgStatLocal.snapshot.fixed_data[PGSTAT_KIND_WAL];
 
 	LWLockAcquire(&stats_shmem->lock, LW_SHARED);
-	memcpy(&pgStatLocal.snapshot.wal, &stats_shmem->stats,
-		   sizeof(pgStatLocal.snapshot.wal));
+	memcpy(stat_snap, &stats_shmem->stats, sizeof(PgStat_WalStats));
 	LWLockRelease(&stats_shmem->lock);
 }
-- 
2.45.2

