From b2463dbbeaf838d17dd1301e8f55c51f7bca2b2d Mon Sep 17 00:00:00 2001
From: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Date: Thu, 13 Aug 2026 12:28:55 +0000
Subject: [PATCH v5 6/6] pgstat: remove PGSTAT_KIND_BACKEND

After WAL, Lock, and IO have moved, PGSTAT_KIND_BACKEND contains no data.
Remove that kind together with its shared structures, callbacks, and call
sites.

The generic per-backend statistics infrastructure can now take over
pgstat_backend.c, whose previous contents implemented the removed kind.
Rename pgstat_per_backend.c accordingly and adjust the build entries.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/anCjoFkGah/uB7lw%40bdtpg
---
 src/backend/utils/activity/Makefile           |   1 -
 src/backend/utils/activity/backend_status.c   |   4 -
 src/backend/utils/activity/meson.build        |   1 -
 src/backend/utils/activity/pgstat.c           |  22 +-
 src/backend/utils/activity/pgstat_backend.c   | 454 +++++++++++++-----
 .../utils/activity/pgstat_per_backend.c       | 407 ----------------
 src/include/pgstat.h                          |  19 -
 src/include/utils/pgstat_internal.h           |  13 -
 src/include/utils/pgstat_kind.h               |  15 +-
 src/test/regress/expected/stats.out           |  17 +-
 src/tools/pgindent/typedefs.list              |   2 -
 11 files changed, 358 insertions(+), 597 deletions(-)
  92.7% src/backend/utils/activity/
   4.3% src/include/

diff --git a/src/backend/utils/activity/Makefile b/src/backend/utils/activity/Makefile
index 8924b10ed2e..2e32d1485d6 100644
--- a/src/backend/utils/activity/Makefile
+++ b/src/backend/utils/activity/Makefile
@@ -29,7 +29,6 @@ OBJS = \
 	pgstat_io.o \
 	pgstat_kind.o \
 	pgstat_lock.o \
-	pgstat_per_backend.o \
 	pgstat_relation.o \
 	pgstat_replslot.o \
 	pgstat_shmem.o \
diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/utils/activity/backend_status.c
index d685fc5cd87..7088fc09651 100644
--- a/src/backend/utils/activity/backend_status.c
+++ b/src/backend/utils/activity/backend_status.c
@@ -458,10 +458,6 @@ pgstat_bestart_final(void)
 
 	PGSTAT_END_WRITE_ACTIVITY(beentry);
 
-	/* Create the backend statistics entry */
-	if (pgstat_tracks_backend_bktype(MyBackendType))
-		pgstat_create_backend(MyProcNumber);
-
 	/* Update app name to current GUC setting */
 	if (application_name)
 		pgstat_report_appname(application_name);
diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/activity/meson.build
index 6434c377e53..e6dcb2e26fc 100644
--- a/src/backend/utils/activity/meson.build
+++ b/src/backend/utils/activity/meson.build
@@ -14,7 +14,6 @@ backend_sources += files(
   'pgstat_io.c',
   'pgstat_kind.c',
   'pgstat_lock.c',
-  'pgstat_per_backend.c',
   'pgstat_relation.c',
   'pgstat_replslot.c',
   'pgstat_shmem.c',
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index 4ab306c06ce..398ec26a298 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -69,7 +69,7 @@
  * To keep things manageable, stats handling is split across several
  * files. Infrastructure pieces are in:
  * - pgstat.c - this file, to tie it all together
- * - pgstat_per_backend.c - generic per-backend statistics infrastructure
+ * - pgstat_backend.c - generic per-backend statistics infrastructure
  * - pgstat_shmem.c - nearly everything dealing with shared memory, including
  *   the maintenance of hashtable entries
  * - pgstat_xact.c - transactional integration, including the transactional
@@ -77,7 +77,6 @@
  *
  * Each statistics kind is handled in a dedicated file:
  * - pgstat_archiver.c
- * - pgstat_backend.c
  * - pgstat_bgwriter.c
  * - pgstat_checkpointer.c
  * - pgstat_database.c
@@ -381,21 +380,6 @@ static const PgStat_KindInfo pgstat_kind_builtin_infos[PGSTAT_KIND_BUILTIN_SIZE]
 		.reset_timestamp_cb = pgstat_subscription_reset_timestamp_cb,
 	},
 
-	[PGSTAT_KIND_BACKEND] = {
-		.name = "backend",
-
-		.fixed_amount = false,
-		.write_to_file = false,
-
-		.accessed_across_databases = true,
-
-		.shared_size = sizeof(PgStatShared_Backend),
-		.shared_data_off = offsetof(PgStatShared_Backend, stats),
-		.shared_data_len = sizeof(((PgStatShared_Backend *) 0)->stats),
-
-		.reset_timestamp_cb = pgstat_backend_reset_timestamp_cb,
-	},
-
 	/* stats for fixed-numbered (mostly 1) objects */
 
 	[PGSTAT_KIND_ARCHIVER] = {
@@ -684,10 +668,6 @@ pgstat_shutdown_hook(int code, Datum arg)
 	Assert(dlist_is_empty(&pgStatPending));
 	dlist_init(&pgStatPending);
 
-	/* drop the backend stats entry */
-	if (!pgstat_drop_entry(PGSTAT_KIND_BACKEND, InvalidOid, MyProcNumber, false))
-		pgstat_request_entry_refs_gc();
-
 	/* Accumulate per-backend stats into the global stats */
 	pgstat_acc_my_per_backend_entries();
 
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index b5996d15d8f..f3f08d578cf 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -1,17 +1,11 @@
 /* -------------------------------------------------------------------------
  *
  * pgstat_backend.c
- *	  Implementation of backend statistics.
+ *	  Generic infrastructure for per-backend statistics.
  *
- * This file contains the implementation of backend statistics.  It is kept
- * separate from pgstat.c to enforce the line between the statistics access /
- * storage implementation and the details about individual types of
- * statistics.
- *
- * This statistics kind uses a proc number as object ID for the hash table
- * of pgstats.  Entries are created each time a process is spawned, and are
- * dropped when the process exits.  These are not written to the pgstats file
- * on disk.
+ * This file manages the per-kind arrays used for per-backend statistics,
+ * including entry creation, fetching, snapshots, transfer to global
+ * statistics, and invalidation.
  *
  * Copyright (c) 2001-2026, PostgreSQL Global Development Group
  *
@@ -23,157 +17,393 @@
 #include "postgres.h"
 
 #include "storage/proc.h"
-#include "storage/procarray.h"
 #include "utils/pgstat_internal.h"
 
 /*
- * Returns statistics of a backend by proc number.
+ * Return the shared-memory object for one built-in fixed-numbered statistics
+ * kind.
  */
-PgStat_Backend *
-pgstat_fetch_stat_backend(ProcNumber procNumber)
+static char *
+pgstat_get_per_backend_shmem_data(PgStat_Kind kind)
 {
-	PgStat_Backend *backend_entry;
+	const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
 
-	backend_entry = (PgStat_Backend *) pgstat_fetch_entry(PGSTAT_KIND_BACKEND,
-														  InvalidOid, procNumber,
-														  NULL);
+	Assert(kind_info != NULL);
+	Assert(kind_info->fixed_amount);
+	Assert(pgstat_is_kind_builtin(kind));
 
-	return backend_entry;
+	return (char *) pgStatLocal.shmem + kind_info->shared_ctl_off;
 }
 
 /*
- * Returns statistics of a backend by pid.
+ * Return one per-backend entry, whether or not it is currently active.
  *
- * This routine includes sanity checks to ensure that the backend exists and
- * is running.  "bktype" can be optionally defined to return the BackendType
- * of the backend whose statistics are returned.
+ * Callers that inspect or modify the entry must hold its content lock.
  */
-PgStat_Backend *
-pgstat_fetch_stat_backend_by_pid(int pid, BackendType *bktype)
+void *
+pgstat_get_per_backend_entry(PgStat_Kind kind, ProcNumber procnum)
 {
-	PGPROC	   *proc;
-	PgBackendStatus *beentry;
-	ProcNumber	procNumber;
-	PgStat_Backend *backend_stats;
+	const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
+	char	   *array;
+	Size		aligned_entry_size;
 
-	proc = BackendPidGetProc(pid);
-	if (bktype)
-		*bktype = B_INVALID;
+	if (kind_info == NULL || kind_info->per_backend_entry_size == 0)
+		return NULL;
 
-	/* this could be an auxiliary process */
-	if (!proc)
-		proc = AuxiliaryPidGetProc(pid);
+	Assert(pgstat_is_kind_builtin(kind));
 
-	if (!proc)
+	if (procnum < 0 || procnum >= NumPerBackendStatSlots)
 		return NULL;
 
-	procNumber = GetNumberFromPGProc(proc);
+	array = pgStatLocal.shmem->per_backend_arrays[kind];
+	Assert(array != NULL);
 
-	beentry = pgstat_get_beentry_by_proc_number(procNumber);
-	if (!beentry)
-		return NULL;
+	/*
+	 * The entry type is kind-specific, so use its aligned size as the byte
+	 * stride for the zero-based ProcNumber.
+	 */
+	aligned_entry_size = MAXALIGN(kind_info->per_backend_entry_size);
 
-	/* check if the backend type tracks statistics */
-	if (!pgstat_tracks_backend_bktype(beentry->st_backendType))
-		return NULL;
+	return array + procnum * aligned_entry_size;
+}
 
-	/* if PID does not match, leave */
-	if (beentry->st_procpid != pid)
-		return NULL;
+/*
+ * Initialize this process's entry for one per-backend statistics kind.
+ *
+ * A blocking flush during early startup may already have initialized the
+ * entry. In that case, preserve the statistics it contains.
+ */
+static PgStatShared_PerBackendEntry *
+pgstat_create_my_per_backend_entry(PgStat_Kind kind)
+{
+	const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
+	PgStatShared_PerBackendEntry *entry;
 
-	if (bktype)
-		*bktype = beentry->st_backendType;
+	entry = pgstat_get_per_backend_entry(kind, MyProcNumber);
 
-	/*
-	 * Retrieve the entry.  Note that "beentry" may be freed depending on the
-	 * value of stats_fetch_consistency, so do not access it from this point.
-	 */
-	backend_stats = pgstat_fetch_stat_backend(procNumber);
-	if (!backend_stats)
+	LWLockAcquire(&entry->lock, LW_EXCLUSIVE);
+
+	if (entry->backend_type == B_INVALID)
 	{
-		if (bktype)
-			*bktype = B_INVALID;
-		return NULL;
+		entry->backend_type = MyBackendType;
+		memset((char *) entry + kind_info->per_backend_data_off, 0,
+			   kind_info->per_backend_data_len);
 	}
 
-	return backend_stats;
+	LWLockRelease(&entry->lock);
+
+	return entry;
 }
 
 /*
- * Create backend statistics entry for proc number.
+ * Initialize entries for all kinds that use per-backend arrays.
+ *
+ * This runs after any stale entries for the reused ProcNumber have been
+ * transferred to global statistics.
  */
 void
-pgstat_create_backend(ProcNumber procnum)
+pgstat_create_my_per_backend_entries(void)
 {
-	PgStat_EntryRef *entry_ref;
-	PgStatShared_Backend *shstatent;
+	for (PgStat_Kind kind = PGSTAT_KIND_BUILTIN_MIN;
+		 kind <= PGSTAT_KIND_BUILTIN_MAX; kind++)
+	{
+		const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
 
-	entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
-											procnum, false);
-	shstatent = (PgStatShared_Backend *) entry_ref->shared_stats;
+		if (kind_info == NULL || kind_info->per_backend_entry_size == 0)
+			continue;
 
-	/*
-	 * NB: need to accept that there might be stats from an older backend,
-	 * e.g. if we previously used this proc number.
-	 */
-	memset(&shstatent->stats, 0, sizeof(shstatent->stats));
-	pgstat_unlock_entry(entry_ref);
+		(void) pgstat_create_my_per_backend_entry(kind);
+	}
 }
 
 /*
- * Backend statistics are not collected for all BackendTypes.
+ * Lock this process's entry for a per-backend statistics kind.
+ *
+ * The blocking path initializes an inactive entry, which allows forced
+ * flushes during early process startup. The routine nonblocking path does not
+ * initialize entries.
  *
- * The following BackendTypes do not participate in the backend stats
- * subsystem:
- * - The same and for the same reasons as in pgstat_tracks_io_bktype().
- * - B_BG_WRITER, B_CHECKPOINTER, B_STARTUP and B_AUTOVAC_LAUNCHER because their
- * I/O stats are already visible in pg_stat_io and there is only one of those.
+ * NB: On success, the entry is returned with its content lock held. A
+ * nonblocking call returns NULL if the entry cannot be locked or is inactive.
+ */
+void *
+pgstat_lock_my_per_backend_entry(PgStat_Kind kind, bool nowait)
+{
+	const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
+	PgStatShared_PerBackendEntry *entry;
+
+	entry = pgstat_get_per_backend_entry(kind, MyProcNumber);
+
+	if (nowait)
+	{
+		if (!LWLockConditionalAcquire(&entry->lock, LW_EXCLUSIVE))
+			return NULL;
+
+		if (entry->backend_type == B_INVALID)
+		{
+			LWLockRelease(&entry->lock);
+			return NULL;
+		}
+	}
+	else
+	{
+		LWLockAcquire(&entry->lock, LW_EXCLUSIVE);
+
+		if (entry->backend_type == B_INVALID)
+		{
+			entry->backend_type = MyBackendType;
+			memset((char *) entry + kind_info->per_backend_data_off, 0,
+				   kind_info->per_backend_data_len);
+		}
+	}
+
+	return entry;
+}
+
+/*
+ * Accumulate all active per-backend entries into a fixed-kind snapshot.
  *
- * Function returns true if BackendType participates in the backend stats
- * subsystem and false if it does not.
+ * SNAPSHOT mode also copies every active entry into the backend-local
+ * snapshot hash, fixing both the values and the set of visible processes.
+ * The caller must hold the kind lock to prevent entries from moving into the
+ * global statistics while the snapshot is assembled.
+ */
+void
+pgstat_per_backend_snapshot(PgStat_Kind kind, void *snap)
+{
+	const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
+	PgStat_HashKey key = {0};
+
+	key.kind = kind;
+	key.dboid = InvalidOid;
+
+	for (ProcNumber procnum = 0; procnum < NumPerBackendStatSlots; procnum++)
+	{
+		PgStatShared_PerBackendEntry *entry;
+
+		entry = pgstat_get_per_backend_entry(kind, procnum);
+
+		LWLockAcquire(&entry->lock, LW_SHARED);
+
+		if (entry->backend_type != B_INVALID)
+		{
+			kind_info->per_backend_acc_cb(snap, entry);
+
+			if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
+			{
+				key.objid = procnum;
+				pgstat_cache_snapshot_entry(&key,
+											(char *) entry +
+											kind_info->per_backend_data_off,
+											kind_info->per_backend_data_len);
+			}
+		}
+
+		LWLockRelease(&entry->lock);
+	}
+}
+
+/*
+ * Fetch one process's per-backend statistics.
  *
- * When adding a new BackendType, also consider adding relevant restrictions to
- * pgstat_tracks_io_object() and pgstat_tracks_io_op().
+ * NONE mode returns a copy allocated in the current memory context. CACHE and
+ * SNAPSHOT modes return data owned by the backend-local statistics snapshot.
+ * Return NULL if the ProcNumber is invalid or no process was active at the
+ * consistency mode's observation point.
  */
-bool
-pgstat_tracks_backend_bktype(BackendType bktype)
+void *
+pgstat_fetch_per_backend(PgStat_Kind kind, ProcNumber procnum)
 {
+	const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
+	PgStatShared_PerBackendEntry *entry;
+	PgStat_HashKey key = {0};
+	void	   *stats_data;
+	bool		found;
+
+	pgstat_prep_snapshot();
+
+	entry = pgstat_get_per_backend_entry(kind, procnum);
+
+	if (entry == NULL)
+		return NULL;
+
+	/* Read directly without caching in NONE mode */
+	if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_NONE)
+	{
+		LWLockAcquire(&entry->lock, LW_SHARED);
+
+		if (entry->backend_type == B_INVALID)
+		{
+			LWLockRelease(&entry->lock);
+			return NULL;
+		}
+
+		stats_data = palloc(kind_info->per_backend_data_len);
+		memcpy(stats_data, (char *) entry + kind_info->per_backend_data_off,
+			   kind_info->per_backend_data_len);
+		LWLockRelease(&entry->lock);
+
+		return stats_data;
+	}
+
+	Assert(pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_CACHE ||
+		   pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT);
+
 	/*
-	 * List every type so that new backend types trigger a warning about
-	 * needing to adjust this switch.
+	 * A full fixed-kind snapshot pre-caches all active entries. CACHE mode
+	 * reads only the requested array entry on a cache miss.
 	 */
-	switch (bktype)
+	if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
+		pgstat_snapshot_fixed(kind);
+	else
+		pgStatLocal.snapshot.mode = PGSTAT_FETCH_CONSISTENCY_CACHE;
+
+	key.kind = kind;
+	key.dboid = InvalidOid;
+	key.objid = procnum;
+
+	stats_data = pgstat_lookup_snapshot_entry(&key, &found);
+
+	if (found)
+		return stats_data;
+
+	/*
+	 * A miss after building a full snapshot means that the process was not
+	 * active at the snapshot point.
+	 */
+	if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
+		return NULL;
+
+	LWLockAcquire(&entry->lock, LW_SHARED);
+
+	if (entry->backend_type == B_INVALID)
+		stats_data = pgstat_cache_snapshot_entry(&key, NULL, 0);
+	else
+		stats_data = pgstat_cache_snapshot_entry(&key,
+												 (char *) entry +
+												 kind_info->per_backend_data_off,
+												 kind_info->per_backend_data_len);
+
+	LWLockRelease(&entry->lock);
+
+	return stats_data;
+}
+
+/*
+ * Transfer this process's entry into global statistics and invalidate it.
+ *
+ * The kind lock serializes this transfer with global snapshots and resets.
+ * The entry lock serializes it with routine flushes and per-backend reads.
+ */
+static void
+pgstat_acc_my_per_backend(PgStat_Kind kind)
+{
+	const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
+	PgStatShared_PerBackendEntry *entry;
+	char	   *shared_struct;
+	LWLock	   *lock;
+	void	   *dst;
+
+	entry = pgstat_get_per_backend_entry(kind, MyProcNumber);
+
+	if (entry == NULL)
+		return;
+
+	shared_struct = pgstat_get_per_backend_shmem_data(kind);
+	lock = (LWLock *) (shared_struct +
+					   kind_info->per_backend_shared_lock_off);
+	dst = shared_struct + kind_info->shared_data_off;
+
+	LWLockAcquire(lock, LW_EXCLUSIVE);
+	LWLockAcquire(&entry->lock, LW_EXCLUSIVE);
+
+	if (entry->backend_type != B_INVALID)
+	{
+		kind_info->per_backend_acc_cb(dst, entry);
+		entry->backend_type = B_INVALID;
+	}
+
+	LWLockRelease(&entry->lock);
+	LWLockRelease(lock);
+}
+
+/*
+ * Transfer this process's entries for all per-backend statistics kinds.
+ *
+ * This is used both at process exit and before reusing a ProcNumber.
+ */
+void
+pgstat_acc_my_per_backend_entries(void)
+{
+	for (PgStat_Kind kind = PGSTAT_KIND_BUILTIN_MIN;
+		 kind <= PGSTAT_KIND_BUILTIN_MAX; kind++)
+	{
+		const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
+
+		if (kind_info == NULL || kind_info->per_backend_entry_size == 0)
+			continue;
+
+		pgstat_acc_my_per_backend(kind);
+	}
+}
+
+/*
+ * Transfer every active entry into global statistics and invalidate it.
+ *
+ * This is called during clean server shutdown before the statistics file is
+ * written.
+ */
+static void
+pgstat_acc_all_per_backend(PgStat_Kind kind)
+{
+	const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
+	char	   *shared_struct;
+	LWLock	   *lock;
+	void	   *dst;
+
+	shared_struct = pgstat_get_per_backend_shmem_data(kind);
+	lock = (LWLock *) (shared_struct +
+					   kind_info->per_backend_shared_lock_off);
+	dst = shared_struct + kind_info->shared_data_off;
+
+	LWLockAcquire(lock, LW_EXCLUSIVE);
+
+	for (ProcNumber procnum = 0; procnum < NumPerBackendStatSlots; procnum++)
 	{
-		case B_INVALID:
-		case B_AUTOVAC_LAUNCHER:
-		case B_DEAD_END_BACKEND:
-		case B_ARCHIVER:
-		case B_LOGGER:
-		case B_BG_WRITER:
-		case B_CHECKPOINTER:
-		case B_IO_WORKER:
-		case B_STARTUP:
-		case B_DATACHECKSUMSWORKER_LAUNCHER:
-		case B_DATACHECKSUMSWORKER_WORKER:
-			return false;
-
-		case B_AUTOVAC_WORKER:
-		case B_BACKEND:
-		case B_BG_WORKER:
-		case B_STANDALONE_BACKEND:
-		case B_SLOTSYNC_WORKER:
-		case B_WAL_RECEIVER:
-		case B_WAL_SENDER:
-		case B_WAL_SUMMARIZER:
-		case B_WAL_WRITER:
-			return true;
+		PgStatShared_PerBackendEntry *entry;
+
+		entry = pgstat_get_per_backend_entry(kind, procnum);
+
+		LWLockAcquire(&entry->lock, LW_EXCLUSIVE);
+
+		if (entry->backend_type != B_INVALID)
+		{
+			kind_info->per_backend_acc_cb(dst, entry);
+			entry->backend_type = B_INVALID;
+		}
+
+		LWLockRelease(&entry->lock);
 	}
 
-	return false;
+	LWLockRelease(lock);
 }
 
+/*
+ * Transfer all entries for all per-backend statistics kinds.
+ *
+ * The caller uses this before writing the statistics file at clean shutdown.
+ */
 void
-pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
+pgstat_acc_all_per_backend_entries(void)
 {
-	((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
+	for (PgStat_Kind kind = PGSTAT_KIND_BUILTIN_MIN;
+		 kind <= PGSTAT_KIND_BUILTIN_MAX; kind++)
+	{
+		const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
+
+		if (kind_info == NULL || kind_info->per_backend_entry_size == 0)
+			continue;
+
+		pgstat_acc_all_per_backend(kind);
+	}
 }
diff --git a/src/backend/utils/activity/pgstat_per_backend.c b/src/backend/utils/activity/pgstat_per_backend.c
deleted file mode 100644
index e866d109fa7..00000000000
--- a/src/backend/utils/activity/pgstat_per_backend.c
+++ /dev/null
@@ -1,407 +0,0 @@
-/* -------------------------------------------------------------------------
- *
- * pgstat_per_backend.c
- *	  Generic infrastructure for per-backend statistics.
- *
- * This file manages the per-kind arrays used for per-backend statistics,
- * including entry creation, fetching, snapshots, transfer to global
- * statistics, and invalidation.
- *
- * Copyright (c) 2001-2026, PostgreSQL Global Development Group
- *
- * IDENTIFICATION
- *	  src/backend/utils/activity/pgstat_per_backend.c
- * -------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "storage/proc.h"
-#include "utils/pgstat_internal.h"
-
-/*
- * Return the shared-memory object for one built-in fixed-numbered statistics
- * kind.
- */
-static char *
-pgstat_get_per_backend_shmem_data(PgStat_Kind kind)
-{
-	const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
-
-	Assert(kind_info != NULL);
-	Assert(kind_info->fixed_amount);
-	Assert(pgstat_is_kind_builtin(kind));
-
-	return (char *) pgStatLocal.shmem + kind_info->shared_ctl_off;
-}
-
-/*
- * Return one per-backend entry, whether or not it is currently active.
- *
- * Callers that inspect or modify the entry must hold its content lock.
- */
-void *
-pgstat_get_per_backend_entry(PgStat_Kind kind, ProcNumber procnum)
-{
-	const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
-	char	   *array;
-	Size		aligned_entry_size;
-
-	if (kind_info == NULL || kind_info->per_backend_entry_size == 0)
-		return NULL;
-
-	Assert(pgstat_is_kind_builtin(kind));
-
-	if (procnum < 0 || procnum >= NumPerBackendStatSlots)
-		return NULL;
-
-	array = pgStatLocal.shmem->per_backend_arrays[kind];
-	Assert(array != NULL);
-
-	/*
-	 * The entry type is kind-specific, so use its aligned size as the byte
-	 * stride for the zero-based ProcNumber.
-	 */
-	aligned_entry_size = MAXALIGN(kind_info->per_backend_entry_size);
-
-	return array + procnum * aligned_entry_size;
-}
-
-/*
- * Initialize this process's entry for one per-backend statistics kind.
- *
- * A blocking flush during early startup may already have initialized the
- * entry. In that case, preserve the statistics it contains.
- */
-static PgStatShared_PerBackendEntry *
-pgstat_create_my_per_backend_entry(PgStat_Kind kind)
-{
-	const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
-	PgStatShared_PerBackendEntry *entry;
-
-	entry = pgstat_get_per_backend_entry(kind, MyProcNumber);
-
-	LWLockAcquire(&entry->lock, LW_EXCLUSIVE);
-
-	if (entry->backend_type == B_INVALID)
-	{
-		entry->backend_type = MyBackendType;
-		memset((char *) entry + kind_info->per_backend_data_off, 0,
-			   kind_info->per_backend_data_len);
-	}
-
-	LWLockRelease(&entry->lock);
-
-	return entry;
-}
-
-/*
- * Initialize entries for all kinds that use per-backend arrays.
- *
- * This runs after any stale entries for the reused ProcNumber have been
- * transferred to global statistics.
- */
-void
-pgstat_create_my_per_backend_entries(void)
-{
-	for (PgStat_Kind kind = PGSTAT_KIND_BUILTIN_MIN;
-		 kind <= PGSTAT_KIND_BUILTIN_MAX; kind++)
-	{
-		const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
-
-		if (kind_info == NULL || kind_info->per_backend_entry_size == 0)
-			continue;
-
-		(void) pgstat_create_my_per_backend_entry(kind);
-	}
-}
-
-/*
- * Lock this process's entry for a per-backend statistics kind.
- *
- * The blocking path initializes an inactive entry, which allows forced
- * flushes during early process startup. The routine nonblocking path does not
- * initialize entries.
- *
- * NB: On success, the entry is returned with its content lock held. A
- * nonblocking call returns NULL if the entry cannot be locked or is inactive.
- */
-void *
-pgstat_lock_my_per_backend_entry(PgStat_Kind kind, bool nowait)
-{
-	const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
-	PgStatShared_PerBackendEntry *entry;
-
-	entry = pgstat_get_per_backend_entry(kind, MyProcNumber);
-
-	if (nowait)
-	{
-		if (!LWLockConditionalAcquire(&entry->lock, LW_EXCLUSIVE))
-			return NULL;
-
-		if (entry->backend_type == B_INVALID)
-		{
-			LWLockRelease(&entry->lock);
-			return NULL;
-		}
-	}
-	else
-	{
-		LWLockAcquire(&entry->lock, LW_EXCLUSIVE);
-
-		if (entry->backend_type == B_INVALID)
-		{
-			entry->backend_type = MyBackendType;
-			memset((char *) entry + kind_info->per_backend_data_off, 0,
-				   kind_info->per_backend_data_len);
-		}
-	}
-
-	return entry;
-}
-
-/*
- * Accumulate all active per-backend entries into a fixed-kind snapshot.
- *
- * SNAPSHOT mode also copies every active entry into the backend-local
- * snapshot hash, fixing both the values and the set of visible processes.
- * The caller must hold the kind lock to prevent entries from moving into the
- * global statistics while the snapshot is assembled.
- */
-void
-pgstat_per_backend_snapshot(PgStat_Kind kind, void *snap)
-{
-	const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
-	PgStat_HashKey key = {0};
-
-	key.kind = kind;
-	key.dboid = InvalidOid;
-
-	for (ProcNumber procnum = 0; procnum < NumPerBackendStatSlots; procnum++)
-	{
-		PgStatShared_PerBackendEntry *entry;
-
-		entry = pgstat_get_per_backend_entry(kind, procnum);
-
-		LWLockAcquire(&entry->lock, LW_SHARED);
-
-		if (entry->backend_type != B_INVALID)
-		{
-			kind_info->per_backend_acc_cb(snap, entry);
-
-			if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
-			{
-				key.objid = procnum;
-				pgstat_cache_snapshot_entry(&key,
-											(char *) entry +
-											kind_info->per_backend_data_off,
-											kind_info->per_backend_data_len);
-			}
-		}
-
-		LWLockRelease(&entry->lock);
-	}
-}
-
-/*
- * Fetch one process's per-backend statistics.
- *
- * NONE mode returns a copy allocated in the current memory context. CACHE and
- * SNAPSHOT modes return data owned by the backend-local statistics snapshot.
- * Return NULL if the ProcNumber is invalid or no process was active at the
- * consistency mode's observation point.
- */
-void *
-pgstat_fetch_per_backend(PgStat_Kind kind, ProcNumber procnum)
-{
-	const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
-	PgStatShared_PerBackendEntry *entry;
-	PgStat_HashKey key = {0};
-	void	   *stats_data;
-	bool		found;
-
-	pgstat_prep_snapshot();
-
-	entry = pgstat_get_per_backend_entry(kind, procnum);
-
-	if (entry == NULL)
-		return NULL;
-
-	/* Read directly without caching in NONE mode */
-	if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_NONE)
-	{
-		LWLockAcquire(&entry->lock, LW_SHARED);
-
-		if (entry->backend_type == B_INVALID)
-		{
-			LWLockRelease(&entry->lock);
-			return NULL;
-		}
-
-		stats_data = palloc(kind_info->per_backend_data_len);
-		memcpy(stats_data, (char *) entry + kind_info->per_backend_data_off,
-			   kind_info->per_backend_data_len);
-		LWLockRelease(&entry->lock);
-
-		return stats_data;
-	}
-
-	Assert(pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_CACHE ||
-		   pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT);
-
-	/*
-	 * A full fixed-kind snapshot pre-caches all active entries. CACHE mode
-	 * reads only the requested array entry on a cache miss.
-	 */
-	if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
-		pgstat_snapshot_fixed(kind);
-	else
-		pgStatLocal.snapshot.mode = PGSTAT_FETCH_CONSISTENCY_CACHE;
-
-	key.kind = kind;
-	key.dboid = InvalidOid;
-	key.objid = procnum;
-
-	stats_data = pgstat_lookup_snapshot_entry(&key, &found);
-
-	if (found)
-		return stats_data;
-
-	/*
-	 * A miss after building a full snapshot means that the process was not
-	 * active at the snapshot point.
-	 */
-	if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
-		return NULL;
-
-	LWLockAcquire(&entry->lock, LW_SHARED);
-
-	if (entry->backend_type == B_INVALID)
-		stats_data = pgstat_cache_snapshot_entry(&key, NULL, 0);
-	else
-		stats_data = pgstat_cache_snapshot_entry(&key,
-												 (char *) entry +
-												 kind_info->per_backend_data_off,
-												 kind_info->per_backend_data_len);
-
-	LWLockRelease(&entry->lock);
-
-	return stats_data;
-}
-
-/*
- * Transfer this process's entry into global statistics and invalidate it.
- *
- * The kind lock serializes this transfer with global snapshots and resets.
- * The entry lock serializes it with routine flushes and per-backend reads.
- */
-static void
-pgstat_acc_my_per_backend(PgStat_Kind kind)
-{
-	const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
-	PgStatShared_PerBackendEntry *entry;
-	char	   *shared_struct;
-	LWLock	   *lock;
-	void	   *dst;
-
-	entry = pgstat_get_per_backend_entry(kind, MyProcNumber);
-
-	if (entry == NULL)
-		return;
-
-	shared_struct = pgstat_get_per_backend_shmem_data(kind);
-	lock = (LWLock *) (shared_struct + kind_info->per_backend_shared_lock_off);
-	dst = shared_struct + kind_info->shared_data_off;
-
-	LWLockAcquire(lock, LW_EXCLUSIVE);
-	LWLockAcquire(&entry->lock, LW_EXCLUSIVE);
-
-	if (entry->backend_type != B_INVALID)
-	{
-		kind_info->per_backend_acc_cb(dst, entry);
-		entry->backend_type = B_INVALID;
-	}
-
-	LWLockRelease(&entry->lock);
-	LWLockRelease(lock);
-}
-
-/*
- * Transfer this process's entries for all per-backend statistics kinds.
- *
- * This is used both at process exit and before reusing a ProcNumber.
- */
-void
-pgstat_acc_my_per_backend_entries(void)
-{
-	for (PgStat_Kind kind = PGSTAT_KIND_BUILTIN_MIN;
-		 kind <= PGSTAT_KIND_BUILTIN_MAX; kind++)
-	{
-		const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
-
-		if (kind_info == NULL || kind_info->per_backend_entry_size == 0)
-			continue;
-
-		pgstat_acc_my_per_backend(kind);
-	}
-}
-
-/*
- * Transfer every active entry into global statistics and invalidate it.
- *
- * This is called during clean server shutdown before the statistics file is
- * written.
- */
-static void
-pgstat_acc_all_per_backend(PgStat_Kind kind)
-{
-	const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
-	char	   *shared_struct;
-	LWLock	   *lock;
-	void	   *dst;
-
-	shared_struct = pgstat_get_per_backend_shmem_data(kind);
-	lock = (LWLock *) (shared_struct + kind_info->per_backend_shared_lock_off);
-	dst = shared_struct + kind_info->shared_data_off;
-
-	LWLockAcquire(lock, LW_EXCLUSIVE);
-
-	for (ProcNumber procnum = 0; procnum < NumPerBackendStatSlots; procnum++)
-	{
-		PgStatShared_PerBackendEntry *entry;
-
-		entry = pgstat_get_per_backend_entry(kind, procnum);
-
-		LWLockAcquire(&entry->lock, LW_EXCLUSIVE);
-
-		if (entry->backend_type != B_INVALID)
-		{
-			kind_info->per_backend_acc_cb(dst, entry);
-			entry->backend_type = B_INVALID;
-		}
-
-		LWLockRelease(&entry->lock);
-	}
-
-	LWLockRelease(lock);
-}
-
-/*
- * Transfer all entries for all per-backend statistics kinds.
- *
- * The caller uses this before writing the statistics file at clean shutdown.
- */
-void
-pgstat_acc_all_per_backend_entries(void)
-{
-	for (PgStat_Kind kind = PGSTAT_KIND_BUILTIN_MIN;
-		 kind <= PGSTAT_KIND_BUILTIN_MAX; kind++)
-	{
-		const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
-
-		if (kind_info == NULL || kind_info->per_backend_entry_size == 0)
-			continue;
-
-		pgstat_acc_all_per_backend(kind);
-	}
-}
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index d6e13ad9838..eb63cdaf5eb 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -594,15 +594,6 @@ typedef struct PgStat_WalStats
 	TimestampTz stat_reset_timestamp;
 } PgStat_WalStats;
 
-/* -------
- * PgStat_Backend		Backend statistics
- * -------
- */
-typedef struct PgStat_Backend
-{
-	TimestampTz stat_reset_timestamp;
-} PgStat_Backend;
-
 /*
  * Functions in pgstat.c
  */
@@ -639,16 +630,6 @@ extern bool pgstat_have_entry(PgStat_Kind kind, Oid dboid, uint64 objid);
 extern void pgstat_report_archiver(const char *xlog, bool failed);
 extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void);
 
-/*
- * Functions in pgstat_backend.c
- */
-
-extern PgStat_Backend *pgstat_fetch_stat_backend(ProcNumber procNumber);
-extern PgStat_Backend *pgstat_fetch_stat_backend_by_pid(int pid,
-														BackendType *bktype);
-extern bool pgstat_tracks_backend_bktype(BackendType bktype);
-extern void pgstat_create_backend(ProcNumber procnum);
-
 /*
  * Functions in pgstat_bgwriter.c
  */
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 9c88f411734..aa1858497ed 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -628,12 +628,6 @@ typedef struct PgStatShared_ReplSlot
 	PgStat_StatReplSlotEntry stats;
 } PgStatShared_ReplSlot;
 
-typedef struct PgStatShared_Backend
-{
-	PgStatShared_Common header;
-	PgStat_Backend stats;
-} PgStatShared_Backend;
-
 /*
  * Central shared memory entry for the cumulative stats system.
  *
@@ -837,13 +831,6 @@ 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);
 
-/*
- * Functions in pgstat_backend.c
- */
-
-extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
-											  TimestampTz ts);
-
 /*
  * Functions in pgstat_bgwriter.c
  */
diff --git a/src/include/utils/pgstat_kind.h b/src/include/utils/pgstat_kind.h
index 45ca599d0dd..489e8fa38bf 100644
--- a/src/include/utils/pgstat_kind.h
+++ b/src/include/utils/pgstat_kind.h
@@ -30,16 +30,15 @@
 #define PGSTAT_KIND_FUNCTION	4	/* per-function statistics */
 #define PGSTAT_KIND_REPLSLOT	5	/* per-slot statistics */
 #define PGSTAT_KIND_SUBSCRIPTION	6	/* per-subscription statistics */
-#define PGSTAT_KIND_BACKEND	7	/* per-backend statistics */
 
 /* stats for fixed-numbered objects */
-#define PGSTAT_KIND_ARCHIVER	8
-#define PGSTAT_KIND_BGWRITER	9
-#define PGSTAT_KIND_CHECKPOINTER	10
-#define PGSTAT_KIND_IO	11
-#define PGSTAT_KIND_LOCK	12
-#define PGSTAT_KIND_SLRU	13
-#define PGSTAT_KIND_WAL	14
+#define PGSTAT_KIND_ARCHIVER	7
+#define PGSTAT_KIND_BGWRITER	8
+#define PGSTAT_KIND_CHECKPOINTER	9
+#define PGSTAT_KIND_IO	10
+#define PGSTAT_KIND_LOCK	11
+#define PGSTAT_KIND_SLRU	12
+#define PGSTAT_KIND_WAL	13
 
 #define PGSTAT_KIND_BUILTIN_MIN PGSTAT_KIND_DATABASE
 #define PGSTAT_KIND_BUILTIN_MAX PGSTAT_KIND_WAL
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 9f1f635234d..4d9e6dd57d3 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -120,15 +120,14 @@ SELECT id, name, fixed_amount,
   4 | function     | f            | f         | t
   5 | replslot     | f            | t         | t
   6 | subscription | f            | t         | t
-  7 | backend      | f            | t         | f
-  8 | archiver     | t            | f         | t
-  9 | bgwriter     | t            | f         | t
- 10 | checkpointer | t            | f         | t
- 11 | io           | t            | f         | t
- 12 | lock         | t            | f         | t
- 13 | slru         | t            | f         | t
- 14 | wal          | t            | f         | t
-(14 rows)
+  7 | archiver     | t            | f         | t
+  8 | bgwriter     | t            | f         | t
+  9 | checkpointer | t            | f         | t
+ 10 | io           | t            | f         | t
+ 11 | lock         | t            | f         | t
+ 12 | slru         | t            | f         | t
+ 13 | wal          | t            | f         | t
+(13 rows)
 
 -- ensure that both seqscan and indexscan plans are allowed
 SET enable_seqscan TO on;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 72b9e76d6d7..83bdece2add 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2290,7 +2290,6 @@ PgFdwSamplingMethod
 PgFdwScanState
 PgIfAddrCallback
 PgStatShared_Archiver
-PgStatShared_Backend
 PgStatShared_BgWriter
 PgStatShared_Checkpointer
 PgStatShared_Common
@@ -2312,7 +2311,6 @@ PgStatShared_Subscription
 PgStatShared_Wal
 PgStatShared_WalBackendEntry
 PgStat_ArchiverStats
-PgStat_Backend
 PgStat_BackendIO
 PgStat_BackendSubEntry
 PgStat_BgWriterStats
-- 
2.34.1

