From 73df7dd739cb2fd98e3745dbd4f65290531a262b Mon Sep 17 00:00:00 2001
From: Jakub Wartak <jakub.wartak@enterprisedb.com>
Date: Fri, 6 Mar 2026 13:29:40 +0100
Subject: [PATCH v7 5/6] Condense PgStat_IO.stats[BACKEND_NUM_TYPES] array by
 using PGSTAT_USED_BACKEND_NUM_TYPES to be more memory efficient.

---
 src/backend/utils/activity/pgstat_io.c | 57 +++++++++++++++++++++++---
 src/backend/utils/adt/pgstatfuncs.c    | 22 ++++++----
 src/include/miscadmin.h                |  2 +-
 src/include/pgstat.h                   |  5 ++-
 4 files changed, 71 insertions(+), 15 deletions(-)

diff --git a/src/backend/utils/activity/pgstat_io.c b/src/backend/utils/activity/pgstat_io.c
index 8605ea65605..1e9bff4da41 100644
--- a/src/backend/utils/activity/pgstat_io.c
+++ b/src/backend/utils/activity/pgstat_io.c
@@ -225,13 +225,14 @@ pgstat_io_flush_cb(bool nowait)
 {
 	LWLock	   *bktype_lock;
 	PgStat_BktypeIO *bktype_shstats;
+	BackendType condensedBkType = pgstat_remap_condensed_bktype(MyBackendType);
 
 	if (!have_iostats)
 		return false;
 
 	bktype_lock = &pgStatLocal.shmem->io.locks[MyBackendType];
 	bktype_shstats =
-		&pgStatLocal.shmem->io.stats.stats[MyBackendType];
+		&pgStatLocal.shmem->io.stats.stats[condensedBkType];
 
 	if (!nowait)
 		LWLockAcquire(bktype_lock, LW_EXCLUSIVE);
@@ -360,7 +361,11 @@ pgstat_io_reset_all_cb(TimestampTz ts)
 	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];
+		BackendType bktype = pgstat_remap_condensed_bktype(i);
+		PgStat_BktypeIO *bktype_shstats;
+		if(bktype == -1)
+			continue;
+		bktype_shstats = &pgStatLocal.shmem->io.stats.stats[bktype];
 
 		LWLockAcquire(bktype_lock, LW_EXCLUSIVE);
 
@@ -386,8 +391,13 @@ pgstat_io_snapshot_cb(void)
 	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];
+		BackendType bktype = pgstat_remap_condensed_bktype(i);
+		PgStat_BktypeIO *bktype_shstats;
+		PgStat_BktypeIO *bktype_snap;
+		if(bktype == -1)
+				continue;
+		bktype_shstats = &pgStatLocal.shmem->io.stats.stats[bktype];
+		bktype_snap = &pgStatLocal.snapshot.io->stats[bktype];
 
 		LWLockAcquire(bktype_lock, LW_SHARED);
 
@@ -419,7 +429,8 @@ pgstat_io_snapshot_cb(void)
 * Function returns true if BackendType participates in the cumulative stats
 * subsystem for IO and false if it does not.
 *
-* When adding a new BackendType, also consider adding relevant restrictions to
+* When adding a new BackendType, ensure that pgstat_remap_condensed_bktype()
+* is updated and also consider adding relevant restrictions to
 * pgstat_tracks_io_object() and pgstat_tracks_io_op().
 */
 bool
@@ -457,6 +468,42 @@ pgstat_tracks_io_bktype(BackendType bktype)
 	return false;
 }
 
+
+/*
+ * Remap sparse backend type IDs to contiguous ones. Keep in sync with enum
+ * BackendType and PGSTAT_USED_BACKEND_NUM_TYPES count.
+ *
+ * Returns -1 if the input ID is invalid or unused.
+ */
+int
+pgstat_remap_condensed_bktype(BackendType bktype) {
+	/* -1 here means it should not be used */
+	static const int mapping_table[BACKEND_NUM_TYPES] = {
+		-1, /* B_INVALID */
+		0,
+		-1, /* B_DEAD_END_BACKEND */
+		1,
+		2,
+		3,
+		4,
+		5,
+		6,
+		-1, /* B_ARCHIVER */
+		7,
+		8,
+		8,
+		10,
+		11,
+		12,
+		13,
+		-1  /* B_LOGGER */
+	};
+
+	if (bktype < 0 || bktype > BACKEND_NUM_TYPES)
+		return -1;
+	return mapping_table[bktype];
+}
+
 /*
  * Some BackendTypes do not perform IO on certain IOObjects or in certain
  * IOContexts. Some IOObjects are never operated on in some IOContexts. Check
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index ac08ab14195..74f1351289a 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1578,9 +1578,12 @@ pg_stat_get_io(PG_FUNCTION_ARGS)
 
 	backends_io_stats = pgstat_fetch_stat_io();
 
-	for (int bktype = 0; bktype < BACKEND_NUM_TYPES; bktype++)
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
 	{
+		BackendType bktype = pgstat_remap_condensed_bktype(i);
 		PgStat_BktypeIO *bktype_stats = &backends_io_stats->stats[bktype];
+		if(bktype == -1)
+			continue;
 
 		/*
 		 * In Assert builds, we can afford an extra loop through all of the
@@ -1588,17 +1591,17 @@ pg_stat_get_io(PG_FUNCTION_ARGS)
 		 * expected stats are non-zero, since it keeps the non-Assert code
 		 * cleaner.
 		 */
-		Assert(pgstat_bktype_io_stats_valid(bktype_stats, bktype));
+		Assert(pgstat_bktype_io_stats_valid(bktype_stats, i));
 
 		/*
 		 * For those BackendTypes without IO Operation stats, skip
 		 * representing them in the view altogether.
 		 */
-		if (!pgstat_tracks_io_bktype(bktype))
+		if (!pgstat_tracks_io_bktype(i))
 			continue;
 
 		/* save tuples with data from this PgStat_BktypeIO */
-		pg_stat_io_build_tuples(rsinfo, bktype_stats, bktype,
+		pg_stat_io_build_tuples(rsinfo, bktype_stats, i,
 								backends_io_stats->stat_reset_timestamp);
 	}
 
@@ -1757,9 +1760,12 @@ pg_stat_get_io_histogram(PG_FUNCTION_ARGS)
 
 	backends_io_stats = pgstat_fetch_stat_io();
 
-	for (int bktype = 0; bktype < BACKEND_NUM_TYPES; bktype++)
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
 	{
+		BackendType bktype = pgstat_remap_condensed_bktype(i);
 		PgStat_BktypeIO *bktype_stats = &backends_io_stats->stats[bktype];
+		if(bktype == -1)
+			continue;
 
 		/*
 		 * In Assert builds, we can afford an extra loop through all of the
@@ -1767,17 +1773,17 @@ pg_stat_get_io_histogram(PG_FUNCTION_ARGS)
 		 * expected stats are non-zero, since it keeps the non-Assert code
 		 * cleaner.
 		 */
-		Assert(pgstat_bktype_io_stats_valid(bktype_stats, bktype));
+		Assert(pgstat_bktype_io_stats_valid(bktype_stats, i));
 
 		/*
 		 * For those BackendTypes without IO Operation stats, skip
 		 * representing them in the view altogether.
 		 */
-		if (!pgstat_tracks_io_bktype(bktype))
+		if (!pgstat_tracks_io_bktype(i))
 			continue;
 
 		/* save tuples with data from this PgStat_BktypeIO */
-		pg_stat_io_histogram_build_tuples(rsinfo, bktype_stats, bktype,
+		pg_stat_io_histogram_build_tuples(rsinfo, bktype_stats, i,
 								backends_io_stats->stat_reset_timestamp);
 	}
 
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index f16f35659b9..d0c62d3248e 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -332,7 +332,7 @@ extern void SwitchBackToLocalLatch(void);
  * MyBackendType indicates what kind of a backend this is.
  *
  * If you add entries, please also update the child_process_kinds array in
- * launch_backend.c.
+ * launch_backend.c and PGSTAT_USED_BACKEND_NUM_TYPES in pgstat.h
  */
 typedef enum BackendType
 {
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 59114f1bc3f..22114d378bd 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -359,10 +359,12 @@ typedef struct PgStat_PendingIO
 
 extern PgStat_PendingIO PendingIOStats;
 
+/* This needs to stay in sync with pgstat_tracks_io_bktype() */
+#define PGSTAT_USED_BACKEND_NUM_TYPES BACKEND_NUM_TYPES - 4
 typedef struct PgStat_IO
 {
 	TimestampTz stat_reset_timestamp;
-	PgStat_BktypeIO stats[BACKEND_NUM_TYPES];
+	PgStat_BktypeIO stats[PGSTAT_USED_BACKEND_NUM_TYPES];
 } PgStat_IO;
 
 typedef struct PgStat_StatDBEntry
@@ -639,6 +641,7 @@ extern const char *pgstat_get_io_context_name(IOContext io_context);
 extern const char *pgstat_get_io_object_name(IOObject io_object);
 extern const char *pgstat_get_io_op_name(IOOp io_op);
 
+extern int pgstat_remap_condensed_bktype(BackendType bktype);
 extern bool pgstat_tracks_io_bktype(BackendType bktype);
 extern bool pgstat_tracks_io_object(BackendType bktype,
 									IOObject io_object, IOContext io_context);
-- 
2.43.0

