On Thu, 2023-01-19 at 16:50 +0530, vignesh C wrote: > > The patch does not apply on top of HEAD as in [1], please post a rebased > patch: > > Regards, > Vignesh
rebased patch attached Thanks, Reid
From b32a346d6e0e00c568e9a285ad15fc2703998c26 Mon Sep 17 00:00:00 2001 From: Reid Thompson <jreidthomp...@nc.rr.com> Date: Sat, 4 Jun 2022 22:23:59 -0400 Subject: [PATCH 2/2] Add the ability to limit the amount of memory that can be allocated to backends. This builds on the work that adds backend memory allocated to pg_stat_activity. Add GUC variable max_total_backend_memory. Specifies a limit to the amount of memory (in MB) that may be allocated to backends in total (i.e. this is not a per user or per backend limit). If unset, or set to 0 it is disabled. It is intended as a resource to help avoid the OOM killer on LINUX and manage resources in general. A backend request that would push the total over the limit will be denied with an out of memory error causing that backend's current query/transaction to fail. Due to the dynamic nature of memory allocations, this limit is not exact. If within 1.5MB of the limit and two backends request 1MB each at the same time both may be allocated, and exceed the limit. Further requests will not be allocated until dropping below the limit. Keep this in mind when setting this value. This limit does not affect auxiliary backend processes. Backend memory allocations are displayed in the pg_stat_activity view. --- doc/src/sgml/config.sgml | 26 +++++ src/backend/storage/ipc/dsm_impl.c | 12 ++ src/backend/utils/activity/backend_status.c | 108 ++++++++++++++++++ src/backend/utils/misc/guc_tables.c | 11 ++ src/backend/utils/misc/postgresql.conf.sample | 3 + src/backend/utils/mmgr/aset.c | 17 +++ src/backend/utils/mmgr/generation.c | 9 ++ src/backend/utils/mmgr/slab.c | 9 +- src/include/utils/backend_status.h | 3 + 9 files changed, 197 insertions(+), 1 deletion(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index f985afc009..51ed4623be 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -2113,6 +2113,32 @@ include_dir 'conf.d' </listitem> </varlistentry> + <varlistentry id="guc-max-total-backend-memory" xreflabel="max_total_backend_memory"> + <term><varname>max_total_backend_memory</varname> (<type>integer</type>) + <indexterm> + <primary><varname>max_total_backend_memory</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + Specifies a limit to the amount of memory (MB) that may be allocated to + backends in total (i.e. this is not a per user or per backend limit). + If unset, or set to 0 it is disabled. A backend request that would + push the total over the limit will be denied with an out of memory + error causing that backend's current query/transaction to fail. Due to + the dynamic nature of memory allocations, this limit is not exact. If + within 1.5MB of the limit and two backends request 1MB each at the same + time both may be allocated, and exceed the limit. Further requests will + not be allocated until dropping below the limit. Keep this in mind when + setting this value. This limit does not affect auxiliary backend + processes <xref linkend="glossary-auxiliary-proc"/> . Backend memory + allocations (<varname>allocated_bytes</varname>) are displayed in the + <link linkend="monitoring-pg-stat-activity-view"><structname>pg_stat_activity</structname></link> + view. + </para> + </listitem> + </varlistentry> + </variablelist> </sect2> diff --git a/src/backend/storage/ipc/dsm_impl.c b/src/backend/storage/ipc/dsm_impl.c index 22885c7bd2..f7047107d5 100644 --- a/src/backend/storage/ipc/dsm_impl.c +++ b/src/backend/storage/ipc/dsm_impl.c @@ -254,6 +254,10 @@ dsm_impl_posix(dsm_op op, dsm_handle handle, Size request_size, return true; } + /* Do not exceed maximum allowed memory allocation */ + if (op == DSM_OP_CREATE && exceeds_max_total_bkend_mem(request_size)) + return false; + /* * Create new segment or open an existing one for attach. * @@ -525,6 +529,10 @@ dsm_impl_sysv(dsm_op op, dsm_handle handle, Size request_size, int flags = IPCProtection; size_t segsize; + /* Do not exceed maximum allowed memory allocation */ + if (op == DSM_OP_CREATE && exceeds_max_total_bkend_mem(request_size)) + return false; + /* * Allocate the memory BEFORE acquiring the resource, so that we don't * leak the resource if memory allocation fails. @@ -719,6 +727,10 @@ dsm_impl_windows(dsm_op op, dsm_handle handle, Size request_size, return true; } + /* Do not exceed maximum allowed memory allocation */ + if (op == DSM_OP_CREATE && exceeds_max_total_bkend_mem(request_size)) + return false; + /* Create new segment or open an existing one for attach. */ if (op == DSM_OP_CREATE) { diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/utils/activity/backend_status.c index 7baf2db57d..da2b5fb042 100644 --- a/src/backend/utils/activity/backend_status.c +++ b/src/backend/utils/activity/backend_status.c @@ -45,6 +45,9 @@ bool pgstat_track_activities = false; int pgstat_track_activity_query_size = 1024; +/* Max backend memory allocation allowed (MB). 0 = disabled */ +int max_total_bkend_mem = 0; + /* exposed so that backend_progress.c can access it */ PgBackendStatus *MyBEEntry = NULL; @@ -1239,3 +1242,108 @@ pgstat_reset_allocated_bytes_storage(void) my_allocated_bytes = &local_my_allocated_bytes; } +/* ---------- + * pgstat_get_all_memory_allocated() - + * + * Return a uint64 representing the current shared memory allocated to all + * backends. This looks directly at the BackendStatusArray, and so will + * provide current information regardless of the age of our transaction's + * snapshot of the status array. + * In the future we will likely utilize additional values - perhaps limit + * backend allocation by user/role, etc. + * ---------- + */ +uint64 +pgstat_get_all_backend_memory_allocated(void) +{ + PgBackendStatus *beentry; + int i; + uint64 all_memory_allocated = 0; + + beentry = BackendStatusArray; + + /* + * We probably shouldn't get here before shared memory has been set up, + * but be safe. + */ + if (beentry == NULL || BackendActivityBuffer == NULL) + return 0; + + /* + * We include AUX procs in all backend memory calculation + */ + for (i = 1; i <= NumBackendStatSlots; i++) + { + /* + * We use a volatile pointer here to ensure the compiler doesn't try + * to get cute. + */ + volatile PgBackendStatus *vbeentry = beentry; + bool found; + uint64 allocated_bytes = 0; + + for (;;) + { + int before_changecount; + int after_changecount; + + pgstat_begin_read_activity(vbeentry, before_changecount); + + /* + * Ignore invalid entries, which may contain invalid data. + * See pgstat_beshutdown_hook() + */ + if (vbeentry->st_procpid > 0) + allocated_bytes = vbeentry->allocated_bytes; + + pgstat_end_read_activity(vbeentry, after_changecount); + + if ((found = pgstat_read_activity_complete(before_changecount, + after_changecount))) + break; + + /* Make sure we can break out of loop if stuck... */ + CHECK_FOR_INTERRUPTS(); + } + + if (found) + all_memory_allocated += allocated_bytes; + + beentry++; + } + + return all_memory_allocated; +} + +/* + * Determine if allocation request will exceed max backend memory allowed. + * Do not apply to auxiliary processes. + */ +bool +exceeds_max_total_bkend_mem(uint64 allocation_request) +{ + bool result = false; + + /* Exclude auxiliary processes from the check */ + if (MyAuxProcType != NotAnAuxProcess) + return result; + + /* Convert max_total_bkend_mem to bytes for comparison */ + if (max_total_bkend_mem && + pgstat_get_all_backend_memory_allocated() + + allocation_request > (uint64) max_total_bkend_mem * 1024 * 1024) + { + /* + * Explicitly identify the OOM being a result of this configuration + * parameter vs a system failure to allocate OOM. + */ + ereport(WARNING, + errmsg("allocation would exceed max_total_memory limit (%llu > %llu)", + (unsigned long long) pgstat_get_all_backend_memory_allocated() + + allocation_request, (unsigned long long) max_total_bkend_mem * 1024 * 1024)); + + result = true; + } + + return result; +} diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 4ac808ed22..d6f3b4e262 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -3467,6 +3467,17 @@ struct config_int ConfigureNamesInt[] = NULL, NULL, NULL }, + { + {"max_total_backend_memory", PGC_SU_BACKEND, RESOURCES_MEM, + gettext_noop("Restrict total backend memory allocations to this max."), + gettext_noop("0 turns this feature off."), + GUC_UNIT_MB + }, + &max_total_bkend_mem, + 0, 0, INT_MAX, + NULL, NULL, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index d06074b86f..bc2d449c87 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -156,6 +156,9 @@ # mmap # (change requires restart) #min_dynamic_shared_memory = 0MB # (change requires restart) +#max_total_backend_memory = 0MB # Restrict total backend memory allocations + # to this max (in MB). 0 turns this feature + # off. # - Disk - diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c index 37e82bcd70..7e50971f58 100644 --- a/src/backend/utils/mmgr/aset.c +++ b/src/backend/utils/mmgr/aset.c @@ -440,6 +440,10 @@ AllocSetContextCreateInternal(MemoryContext parent, else firstBlockSize = Max(firstBlockSize, initBlockSize); + /* Do not exceed maximum allowed memory allocation */ + if (exceeds_max_total_bkend_mem(firstBlockSize)) + return NULL; + /* * Allocate the initial block. Unlike other aset.c blocks, it starts with * the context header and its block header follows that. @@ -741,6 +745,11 @@ AllocSetAlloc(MemoryContext context, Size size) #endif blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; + + /* Do not exceed maximum allowed memory allocation */ + if (exceeds_max_total_bkend_mem(blksize)) + return NULL; + block = (AllocBlock) malloc(blksize); if (block == NULL) return NULL; @@ -938,6 +947,10 @@ AllocSetAlloc(MemoryContext context, Size size) while (blksize < required_size) blksize <<= 1; + /* Do not exceed maximum allowed memory allocation */ + if (exceeds_max_total_bkend_mem(blksize)) + return NULL; + /* Try to allocate it */ block = (AllocBlock) malloc(blksize); @@ -1176,6 +1189,10 @@ AllocSetRealloc(void *pointer, Size size) blksize = chksize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; oldblksize = block->endptr - ((char *) block); + /* Do not exceed maximum allowed memory allocation */ + if (blksize > oldblksize && exceeds_max_total_bkend_mem(blksize - oldblksize)) + return NULL; + block = (AllocBlock) realloc(block, blksize); if (block == NULL) { diff --git a/src/backend/utils/mmgr/generation.c b/src/backend/utils/mmgr/generation.c index b06fb0c6a4..18d43d52bd 100644 --- a/src/backend/utils/mmgr/generation.c +++ b/src/backend/utils/mmgr/generation.c @@ -201,6 +201,9 @@ GenerationContextCreate(MemoryContext parent, else allocSize = Max(allocSize, initBlockSize); + if (exceeds_max_total_bkend_mem(allocSize)) + return NULL; + /* * Allocate the initial block. Unlike other generation.c blocks, it * starts with the context header and its block header follows that. @@ -380,6 +383,9 @@ GenerationAlloc(MemoryContext context, Size size) { Size blksize = required_size + Generation_BLOCKHDRSZ; + if (exceeds_max_total_bkend_mem(blksize)) + return NULL; + block = (GenerationBlock *) malloc(blksize); if (block == NULL) return NULL; @@ -483,6 +489,9 @@ GenerationAlloc(MemoryContext context, Size size) if (blksize < required_size) blksize = pg_nextpower2_size_t(required_size); + if (exceeds_max_total_bkend_mem(blksize)) + return NULL; + block = (GenerationBlock *) malloc(blksize); if (block == NULL) diff --git a/src/backend/utils/mmgr/slab.c b/src/backend/utils/mmgr/slab.c index e314f8f343..adc88e0047 100644 --- a/src/backend/utils/mmgr/slab.c +++ b/src/backend/utils/mmgr/slab.c @@ -356,9 +356,12 @@ SlabContextCreate(MemoryContext parent, elog(ERROR, "block size %zu for slab is too small for %zu-byte chunks", blockSize, chunkSize); - + /* Do not exceed maximum allowed memory allocation */ + if (exceeds_max_total_bkend_mem(Slab_CONTEXT_HDRSZ(chunksPerBlock))) + return NULL; slab = (SlabContext *) malloc(Slab_CONTEXT_HDRSZ(chunksPerBlock)); + if (slab == NULL) { MemoryContextStats(TopMemoryContext); @@ -559,6 +562,10 @@ SlabAlloc(MemoryContext context, Size size) } else { + /* Do not exceed maximum allowed memory allocation */ + if (exceeds_max_total_bkend_mem(slab->blockSize)) + return NULL; + block = (SlabBlock *) malloc(slab->blockSize); if (unlikely(block == NULL)) diff --git a/src/include/utils/backend_status.h b/src/include/utils/backend_status.h index 754ff0dc62..33269eb11b 100644 --- a/src/include/utils/backend_status.h +++ b/src/include/utils/backend_status.h @@ -297,6 +297,7 @@ typedef struct LocalPgBackendStatus */ extern PGDLLIMPORT bool pgstat_track_activities; extern PGDLLIMPORT int pgstat_track_activity_query_size; +extern PGDLLIMPORT int max_total_bkend_mem; /* ---------- @@ -336,6 +337,7 @@ extern const char *pgstat_get_backend_current_activity(int pid, bool checkUser); extern const char *pgstat_get_crashed_backend_activity(int pid, char *buffer, int buflen); extern uint64 pgstat_get_my_query_id(void); +extern uint64 pgstat_get_all_backend_memory_allocated(void); extern void pgstat_set_allocated_bytes_storage(uint64 *allocated_bytes); extern void pgstat_reset_allocated_bytes_storage(void); @@ -348,6 +350,7 @@ extern int pgstat_fetch_stat_numbackends(void); extern PgBackendStatus *pgstat_fetch_stat_beentry(BackendId beid); extern LocalPgBackendStatus *pgstat_fetch_stat_local_beentry(int beid); extern char *pgstat_clip_activity(const char *raw_activity); +extern bool exceeds_max_total_bkend_mem(uint64 allocation_request); /* ---------- * pgstat_report_allocated_bytes() - -- 2.25.1
From fe8ea9bc008df25f439048d6c653de8439ce5baa Mon Sep 17 00:00:00 2001 From: Reid Thompson <jreidthomp...@nc.rr.com> Date: Thu, 11 Aug 2022 12:01:25 -0400 Subject: [PATCH 1/2] Add tracking of backend memory allocated to pg_stat_activity This new field displays the current bytes of memory allocated to the backend process. It is updated as memory for the process is palloc'd/pfree'd. Memory allocated to items on the freelist is included in the displayed value. Dynamic shared memory allocations are included only in the value displayed for the backend that created them, they are not included in the value for backends that are attached to them to avoid double counting. On occasion, orphaned memory segments may be cleaned up on postmaster startup. This may result in decreasing the sum without a prior increment. We limit the floor of backend_mem_allocated to zero. Updated pg_stat_activity documentation for the new column. --- doc/src/sgml/monitoring.sgml | 15 ++++ src/backend/catalog/system_views.sql | 1 + src/backend/postmaster/autovacuum.c | 6 ++ src/backend/postmaster/postmaster.c | 13 ++++ src/backend/postmaster/syslogger.c | 3 + src/backend/storage/ipc/dsm_impl.c | 81 +++++++++++++++++++++ src/backend/utils/activity/backend_status.c | 45 ++++++++++++ src/backend/utils/adt/pgstatfuncs.c | 4 +- src/backend/utils/mmgr/aset.c | 17 +++++ src/backend/utils/mmgr/generation.c | 15 ++++ src/backend/utils/mmgr/slab.c | 22 ++++++ src/include/catalog/pg_proc.dat | 6 +- src/include/utils/backend_status.h | 63 +++++++++++++++- src/test/regress/expected/rules.out | 9 ++- src/test/regress/expected/stats.out | 11 +++ src/test/regress/sql/stats.sql | 3 + 16 files changed, 305 insertions(+), 9 deletions(-) diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 1756f1a4b6..844d9019dd 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -957,6 +957,21 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser </para></entry> </row> + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>allocated_bytes</structfield> <type>bigint</type> + </para> + <para> + Memory currently allocated to this backend in bytes. This is the balance + of bytes allocated and freed by this backend. Dynamic shared memory + allocations are included only in the value displayed for the backend that + created them, they are not included in the value for backends that are + attached to them to avoid double counting. Use <function>pg_size_pretty</function> + described in <xref linkend="functions-admin-dbsize"/> to make this value + more easily readable. + </para></entry> + </row> + <row> <entry role="catalog_table_entry"><para role="column_definition"> <structfield>query</structfield> <type>text</type> diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 8608e3fa5b..aacd269b01 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -863,6 +863,7 @@ CREATE VIEW pg_stat_activity AS S.state, S.backend_xid, s.backend_xmin, + S.allocated_bytes, S.query_id, S.query, S.backend_type diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index f5ea381c53..09f5624ade 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -407,6 +407,9 @@ StartAutoVacLauncher(void) #ifndef EXEC_BACKEND case 0: + /* Zero allocated bytes to avoid double counting parent allocation */ + pgstat_zero_my_allocated_bytes(); + /* in postmaster child ... */ InitPostmasterChild(); @@ -1485,6 +1488,9 @@ StartAutoVacWorker(void) #ifndef EXEC_BACKEND case 0: + /* Zero allocated bytes to avoid double counting parent allocation */ + pgstat_zero_my_allocated_bytes(); + /* in postmaster child ... */ InitPostmasterChild(); diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 711efc35e3..226d6c0a6f 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -4161,6 +4161,9 @@ BackendStartup(Port *port) { free(bn); + /* Zero allocated bytes to avoid double counting parent allocation */ + pgstat_zero_my_allocated_bytes(); + /* Detangle from postmaster */ InitPostmasterChild(); @@ -5368,6 +5371,11 @@ StartChildProcess(AuxProcType type) MemoryContextDelete(PostmasterContext); PostmasterContext = NULL; + /* Zero allocated bytes to avoid double counting parent allocation. + * Needs to be after the MemoryContextDelete(PostmasterContext) above. + */ + pgstat_zero_my_allocated_bytes(); + AuxiliaryProcessMain(type); /* does not return */ } #endif /* EXEC_BACKEND */ @@ -5761,6 +5769,11 @@ do_start_bgworker(RegisteredBgWorker *rw) MemoryContextDelete(PostmasterContext); PostmasterContext = NULL; + /* Zero allocated bytes to avoid double counting parent allocation. + * Needs to be after the MemoryContextDelete(PostmasterContext) above. + */ + pgstat_zero_my_allocated_bytes(); + StartBackgroundWorker(); exit(1); /* should not get here */ diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c index a876d02c6f..0d51af6fd8 100644 --- a/src/backend/postmaster/syslogger.c +++ b/src/backend/postmaster/syslogger.c @@ -679,6 +679,9 @@ SysLogger_Start(void) #ifndef EXEC_BACKEND case 0: + /* Zero allocated bytes to avoid double counting parent allocation */ + pgstat_zero_my_allocated_bytes(); + /* in postmaster child ... */ InitPostmasterChild(); diff --git a/src/backend/storage/ipc/dsm_impl.c b/src/backend/storage/ipc/dsm_impl.c index f0965c3481..22885c7bd2 100644 --- a/src/backend/storage/ipc/dsm_impl.c +++ b/src/backend/storage/ipc/dsm_impl.c @@ -66,6 +66,7 @@ #include "postmaster/postmaster.h" #include "storage/dsm_impl.h" #include "storage/fd.h" +#include "utils/backend_status.h" #include "utils/guc.h" #include "utils/memutils.h" @@ -232,6 +233,14 @@ dsm_impl_posix(dsm_op op, dsm_handle handle, Size request_size, name))); return false; } + + /* + * Detach and destroy pass through here, only decrease the memory + * shown allocated in pg_stat_activity when the creator destroys the + * allocation. + */ + if (op == DSM_OP_DESTROY) + pgstat_report_allocated_bytes(*mapped_size, PG_ALLOC_DECREASE); *mapped_address = NULL; *mapped_size = 0; if (op == DSM_OP_DESTROY && shm_unlink(name) != 0) @@ -332,6 +341,36 @@ dsm_impl_posix(dsm_op op, dsm_handle handle, Size request_size, name))); return false; } + + /* + * Attach and create pass through here, only update backend memory + * allocated in pg_stat_activity for the creator process. + */ + if (op == DSM_OP_CREATE) + { + /* + * Posix creation calls dsm_impl_posix_resize implying that resizing + * occurs or may be added in the future. As implemented + * dsm_impl_posix_resize utilizes fallocate or truncate, passing the + * whole new size as input, growing the allocation as needed (only + * truncate supports shrinking). We update by replacing the old + * allocation with the new. + */ +#if defined(HAVE_POSIX_FALLOCATE) && defined(__linux__) + /* + * posix_fallocate does not shrink allocations, adjust only on + * allocation increase. + */ + if (request_size > *mapped_size) + { + pgstat_report_allocated_bytes(request_size - *mapped_size, + PG_ALLOC_INCREASE); + } +#else + pgstat_report_allocated_bytes(*mapped_size, PG_ALLOC_DECREASE); + pgstat_report_allocated_bytes(request_size, PG_ALLOC_INCREASE); +#endif + } *mapped_address = address; *mapped_size = request_size; close(fd); @@ -537,6 +576,14 @@ dsm_impl_sysv(dsm_op op, dsm_handle handle, Size request_size, name))); return false; } + + /* + * Detach and destroy pass through here, only decrease the memory + * shown allocated in pg_stat_activity when the creator destroys the + * allocation. + */ + if (op == DSM_OP_DESTROY) + pgstat_report_allocated_bytes(*mapped_size, PG_ALLOC_DECREASE); *mapped_address = NULL; *mapped_size = 0; if (op == DSM_OP_DESTROY && shmctl(ident, IPC_RMID, NULL) < 0) @@ -584,6 +631,13 @@ dsm_impl_sysv(dsm_op op, dsm_handle handle, Size request_size, name))); return false; } + + /* + * Attach and create pass through here, only update backend memory + * allocated in pg_stat_activity for the creator process. + */ + if (op == DSM_OP_CREATE) + pgstat_report_allocated_bytes(request_size, PG_ALLOC_INCREASE); *mapped_address = address; *mapped_size = request_size; @@ -652,6 +706,13 @@ dsm_impl_windows(dsm_op op, dsm_handle handle, Size request_size, return false; } + /* + * Detach and destroy pass through here, only decrease the memory + * shown allocated in pg_stat_activity when the creator destroys the + * allocation. + */ + if (op == DSM_OP_DESTROY) + pgstat_report_allocated_bytes(*mapped_size, PG_ALLOC_DECREASE); *impl_private = NULL; *mapped_address = NULL; *mapped_size = 0; @@ -768,6 +829,12 @@ dsm_impl_windows(dsm_op op, dsm_handle handle, Size request_size, return false; } + /* + * Attach and create pass through here, only update backend memory + * allocated in pg_stat_activity for the creator process. + */ + if (op == DSM_OP_CREATE) + pgstat_report_allocated_bytes(info.RegionSize, PG_ALLOC_INCREASE); *mapped_address = address; *mapped_size = info.RegionSize; *impl_private = hmap; @@ -812,6 +879,13 @@ dsm_impl_mmap(dsm_op op, dsm_handle handle, Size request_size, name))); return false; } + + /* + * Detach and destroy pass through here, only decrease the memory + * shown allocated in pg_stat_activity when the creator destroys the + * allocation. + */ + pgstat_report_allocated_bytes(*mapped_size, PG_ALLOC_DECREASE); *mapped_address = NULL; *mapped_size = 0; if (op == DSM_OP_DESTROY && unlink(name) != 0) @@ -933,6 +1007,13 @@ dsm_impl_mmap(dsm_op op, dsm_handle handle, Size request_size, name))); return false; } + + /* + * Attach and create pass through here, only update backend memory + * allocated in pg_stat_activity for the creator process. + */ + if (op == DSM_OP_CREATE) + pgstat_report_allocated_bytes(request_size, PG_ALLOC_INCREASE); *mapped_address = address; *mapped_size = request_size; diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/utils/activity/backend_status.c index 608d01ea0d..7baf2db57d 100644 --- a/src/backend/utils/activity/backend_status.c +++ b/src/backend/utils/activity/backend_status.c @@ -49,6 +49,9 @@ int pgstat_track_activity_query_size = 1024; /* exposed so that backend_progress.c can access it */ PgBackendStatus *MyBEEntry = NULL; +/* Memory allocated to this backend prior to pgstats initialization */ +uint64 local_my_allocated_bytes = 0; +uint64 *my_allocated_bytes = &local_my_allocated_bytes; static PgBackendStatus *BackendStatusArray = NULL; static char *BackendAppnameBuffer = NULL; @@ -400,6 +403,15 @@ pgstat_bestart(void) lbeentry.st_progress_command_target = InvalidOid; lbeentry.st_query_id = UINT64CONST(0); + /* Alter allocation reporting from local_my_allocated_bytes to shared memory */ + pgstat_set_allocated_bytes_storage(&MyBEEntry->allocated_bytes); + + /* Populate sum of memory allocated prior to pgstats initialization to pgstats + * and zero the local variable. + */ + lbeentry.allocated_bytes += local_my_allocated_bytes; + local_my_allocated_bytes = 0; + /* * we don't zero st_progress_param here to save cycles; nobody should * examine it until st_progress_command has been set to something other @@ -459,6 +471,11 @@ pgstat_beshutdown_hook(int code, Datum arg) { volatile PgBackendStatus *beentry = MyBEEntry; + /* + * Stop reporting memory allocation changes to &MyBEEntry->allocated_bytes + */ + pgstat_reset_allocated_bytes_storage(); + /* * Clear my status entry, following the protocol of bumping st_changecount * before and after. We use a volatile pointer here to ensure the @@ -1194,3 +1211,31 @@ pgstat_clip_activity(const char *raw_activity) return activity; } + +/* + * Configure bytes allocated reporting to report allocated bytes to + * *allocated_bytes. *allocated_bytes needs to be valid until + * pgstat_set_allocated_bytes_storage() is called. + * + * Expected to be called during backend startup (in pgstat_bestart), to point + * my_allocated_bytes into shared memory. + */ +void +pgstat_set_allocated_bytes_storage(uint64 *new_allocated_bytes) +{ + my_allocated_bytes = new_allocated_bytes; + *new_allocated_bytes = local_my_allocated_bytes; +} + +/* + * Reset allocated bytes storage location. + * + * Expected to be called during backend shutdown, before the location set up + * by pgstat_set_allocated_bytes_storage() becomes invalid. + */ +void +pgstat_reset_allocated_bytes_storage(void) +{ + my_allocated_bytes = &local_my_allocated_bytes; +} + diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 6737493402..f4f2c8dce1 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -303,7 +303,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS) Datum pg_stat_get_activity(PG_FUNCTION_ARGS) { -#define PG_STAT_GET_ACTIVITY_COLS 30 +#define PG_STAT_GET_ACTIVITY_COLS 31 int num_backends = pgstat_fetch_stat_numbackends(); int curr_backend; int pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0); @@ -359,6 +359,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) else nulls[16] = true; + values[30] = UInt64GetDatum(beentry->allocated_bytes); + /* Values only available to role member or pg_read_all_stats */ if (HAS_PGSTAT_PERMISSIONS(beentry->st_userid)) { diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c index 740729b5d0..37e82bcd70 100644 --- a/src/backend/utils/mmgr/aset.c +++ b/src/backend/utils/mmgr/aset.c @@ -47,6 +47,7 @@ #include "postgres.h" #include "port/pg_bitutils.h" +#include "utils/backend_status.h" #include "utils/memdebug.h" #include "utils/memutils.h" #include "utils/memutils_memorychunk.h" @@ -521,6 +522,7 @@ AllocSetContextCreateInternal(MemoryContext parent, name); ((MemoryContext) set)->mem_allocated = firstBlockSize; + pgstat_report_allocated_bytes(firstBlockSize, PG_ALLOC_INCREASE); return (MemoryContext) set; } @@ -543,6 +545,7 @@ AllocSetReset(MemoryContext context) AllocSet set = (AllocSet) context; AllocBlock block; Size keepersize PG_USED_FOR_ASSERTS_ONLY; + uint64 deallocation = 0; Assert(AllocSetIsValid(set)); @@ -585,6 +588,7 @@ AllocSetReset(MemoryContext context) { /* Normal case, release the block */ context->mem_allocated -= block->endptr - ((char *) block); + deallocation += block->endptr - ((char *) block); #ifdef CLOBBER_FREED_MEMORY wipe_mem(block, block->freeptr - ((char *) block)); @@ -595,6 +599,7 @@ AllocSetReset(MemoryContext context) } Assert(context->mem_allocated == keepersize); + pgstat_report_allocated_bytes(deallocation, PG_ALLOC_DECREASE); /* Reset block size allocation sequence, too */ set->nextBlockSize = set->initBlockSize; @@ -613,6 +618,7 @@ AllocSetDelete(MemoryContext context) AllocSet set = (AllocSet) context; AllocBlock block = set->blocks; Size keepersize PG_USED_FOR_ASSERTS_ONLY; + uint64 deallocation = 0; Assert(AllocSetIsValid(set)); @@ -651,11 +657,13 @@ AllocSetDelete(MemoryContext context) freelist->first_free = (AllocSetContext *) oldset->header.nextchild; freelist->num_free--; + deallocation += oldset->header.mem_allocated; /* All that remains is to free the header/initial block */ free(oldset); } Assert(freelist->num_free == 0); + pgstat_report_allocated_bytes(deallocation, PG_ALLOC_DECREASE); } /* Now add the just-deleted context to the freelist. */ @@ -672,7 +680,10 @@ AllocSetDelete(MemoryContext context) AllocBlock next = block->next; if (block != set->keeper) + { context->mem_allocated -= block->endptr - ((char *) block); + deallocation += block->endptr - ((char *) block); + } #ifdef CLOBBER_FREED_MEMORY wipe_mem(block, block->freeptr - ((char *) block)); @@ -685,6 +696,7 @@ AllocSetDelete(MemoryContext context) } Assert(context->mem_allocated == keepersize); + pgstat_report_allocated_bytes(deallocation + context->mem_allocated, PG_ALLOC_DECREASE); /* Finally, free the context header, including the keeper block */ free(set); @@ -734,6 +746,7 @@ AllocSetAlloc(MemoryContext context, Size size) return NULL; context->mem_allocated += blksize; + pgstat_report_allocated_bytes(blksize, PG_ALLOC_INCREASE); block->aset = set; block->freeptr = block->endptr = ((char *) block) + blksize; @@ -944,6 +957,7 @@ AllocSetAlloc(MemoryContext context, Size size) return NULL; context->mem_allocated += blksize; + pgstat_report_allocated_bytes(blksize, PG_ALLOC_INCREASE); block->aset = set; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; @@ -1041,6 +1055,7 @@ AllocSetFree(void *pointer) block->next->prev = block->prev; set->header.mem_allocated -= block->endptr - ((char *) block); + pgstat_report_allocated_bytes(block->endptr - ((char *) block), PG_ALLOC_DECREASE); #ifdef CLOBBER_FREED_MEMORY wipe_mem(block, block->freeptr - ((char *) block)); @@ -1171,7 +1186,9 @@ AllocSetRealloc(void *pointer, Size size) /* updated separately, not to underflow when (oldblksize > blksize) */ set->header.mem_allocated -= oldblksize; + pgstat_report_allocated_bytes(oldblksize, PG_ALLOC_DECREASE); set->header.mem_allocated += blksize; + pgstat_report_allocated_bytes(blksize, PG_ALLOC_INCREASE); block->freeptr = block->endptr = ((char *) block) + blksize; diff --git a/src/backend/utils/mmgr/generation.c b/src/backend/utils/mmgr/generation.c index ebcb61e9b6..b06fb0c6a4 100644 --- a/src/backend/utils/mmgr/generation.c +++ b/src/backend/utils/mmgr/generation.c @@ -37,6 +37,7 @@ #include "lib/ilist.h" #include "port/pg_bitutils.h" +#include "utils/backend_status.h" #include "utils/memdebug.h" #include "utils/memutils.h" #include "utils/memutils_memorychunk.h" @@ -267,6 +268,7 @@ GenerationContextCreate(MemoryContext parent, name); ((MemoryContext) set)->mem_allocated = firstBlockSize; + pgstat_report_allocated_bytes(firstBlockSize, PG_ALLOC_INCREASE); return (MemoryContext) set; } @@ -283,6 +285,7 @@ GenerationReset(MemoryContext context) { GenerationContext *set = (GenerationContext *) context; dlist_mutable_iter miter; + uint64 deallocation = 0; Assert(GenerationIsValid(set)); @@ -305,9 +308,14 @@ GenerationReset(MemoryContext context) if (block == set->keeper) GenerationBlockMarkEmpty(block); else + { + deallocation += block->blksize; GenerationBlockFree(set, block); + } } + pgstat_report_allocated_bytes(deallocation, PG_ALLOC_DECREASE); + /* set it so new allocations to make use of the keeper block */ set->block = set->keeper; @@ -328,6 +336,9 @@ GenerationDelete(MemoryContext context) { /* Reset to release all releasable GenerationBlocks */ GenerationReset(context); + + pgstat_report_allocated_bytes(context->mem_allocated, PG_ALLOC_DECREASE); + /* And free the context header and keeper block */ free(context); } @@ -374,6 +385,7 @@ GenerationAlloc(MemoryContext context, Size size) return NULL; context->mem_allocated += blksize; + pgstat_report_allocated_bytes(blksize, PG_ALLOC_INCREASE); /* block with a single (used) chunk */ block->context = set; @@ -477,6 +489,7 @@ GenerationAlloc(MemoryContext context, Size size) return NULL; context->mem_allocated += blksize; + pgstat_report_allocated_bytes(blksize, PG_ALLOC_INCREASE); /* initialize the new block */ GenerationBlockInit(set, block, blksize); @@ -729,6 +742,8 @@ GenerationFree(void *pointer) dlist_delete(&block->node); set->header.mem_allocated -= block->blksize; + pgstat_report_allocated_bytes(block->blksize, PG_ALLOC_DECREASE); + free(block); } diff --git a/src/backend/utils/mmgr/slab.c b/src/backend/utils/mmgr/slab.c index 33dca0f37c..e314f8f343 100644 --- a/src/backend/utils/mmgr/slab.c +++ b/src/backend/utils/mmgr/slab.c @@ -69,6 +69,7 @@ #include "postgres.h" #include "lib/ilist.h" +#include "utils/backend_status.h" #include "utils/memdebug.h" #include "utils/memutils.h" #include "utils/memutils_memorychunk.h" @@ -413,6 +414,13 @@ SlabContextCreate(MemoryContext parent, parent, name); + /* + * If SlabContextCreate is updated to add context header size to + * context->mem_allocated, then update here and SlabDelete appropriately + */ + pgstat_report_allocated_bytes(Slab_CONTEXT_HDRSZ(slab->chunksPerBlock), + PG_ALLOC_INCREASE); + return (MemoryContext) slab; } @@ -429,6 +437,7 @@ SlabReset(MemoryContext context) SlabContext *slab = (SlabContext *) context; dlist_mutable_iter miter; int i; + uint64 deallocation = 0; Assert(SlabIsValid(slab)); @@ -465,9 +474,11 @@ SlabReset(MemoryContext context) #endif free(block); context->mem_allocated -= slab->blockSize; + deallocation += slab->blockSize; } } + pgstat_report_allocated_bytes(deallocation, PG_ALLOC_DECREASE); slab->curBlocklistIndex = 0; Assert(context->mem_allocated == 0); @@ -480,8 +491,17 @@ SlabReset(MemoryContext context) void SlabDelete(MemoryContext context) { + /* Reset to release all the SlabBlocks */ SlabReset(context); + + /* + * Until context header allocation is included in context->mem_allocated, + * cast to slab and decrement the header allocation + */ + pgstat_report_allocated_bytes(Slab_CONTEXT_HDRSZ(((SlabContext *)context)->chunksPerBlock), + PG_ALLOC_DECREASE); + /* And free the context header */ free(context); } @@ -546,6 +566,7 @@ SlabAlloc(MemoryContext context, Size size) block->slab = slab; context->mem_allocated += slab->blockSize; + pgstat_report_allocated_bytes(slab->blockSize, PG_ALLOC_INCREASE); /* use the first chunk in the new block */ chunk = SlabBlockGetChunk(slab, block, 0); @@ -732,6 +753,7 @@ SlabFree(void *pointer) #endif free(block); slab->header.mem_allocated -= slab->blockSize; + pgstat_report_allocated_bytes(slab->blockSize, PG_ALLOC_DECREASE); } /* diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index c0f2a8a77c..3fc9a30c60 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -5404,9 +5404,9 @@ proname => 'pg_stat_get_activity', prorows => '100', proisstrict => 'f', proretset => 't', provolatile => 's', proparallel => 'r', prorettype => 'record', proargtypes => 'int4', - proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,text,numeric,text,bool,text,bool,int4,int8}', - proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}', - proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,leader_pid,query_id}', + proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,text,numeric,text,bool,text,bool,int4,int8,int8}', + proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}', + proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,leader_pid,query_id,allocated_bytes}', prosrc => 'pg_stat_get_activity' }, { oid => '3318', descr => 'statistics: information about progress of backends running maintenance command', diff --git a/src/include/utils/backend_status.h b/src/include/utils/backend_status.h index f7bd83113a..754ff0dc62 100644 --- a/src/include/utils/backend_status.h +++ b/src/include/utils/backend_status.h @@ -15,6 +15,7 @@ #include "miscadmin.h" /* for BackendType */ #include "storage/backendid.h" #include "utils/backend_progress.h" +#include "common/int.h" /* ---------- @@ -32,6 +33,13 @@ typedef enum BackendState STATE_DISABLED } BackendState; +/* Enum helper for reporting memory allocated bytes */ +enum allocation_direction +{ + PG_ALLOC_DECREASE = -1, + PG_ALLOC_IGNORE, + PG_ALLOC_INCREASE, +}; /* ---------- * Shared-memory data structures @@ -169,6 +177,9 @@ typedef struct PgBackendStatus /* query identifier, optionally computed using post_parse_analyze_hook */ uint64 st_query_id; + + /* Current memory allocated to this backend */ + uint64 allocated_bytes; } PgBackendStatus; @@ -293,6 +304,7 @@ extern PGDLLIMPORT int pgstat_track_activity_query_size; * ---------- */ extern PGDLLIMPORT PgBackendStatus *MyBEEntry; +extern PGDLLIMPORT uint64 *my_allocated_bytes; /* ---------- @@ -324,7 +336,8 @@ extern const char *pgstat_get_backend_current_activity(int pid, bool checkUser); extern const char *pgstat_get_crashed_backend_activity(int pid, char *buffer, int buflen); extern uint64 pgstat_get_my_query_id(void); - +extern void pgstat_set_allocated_bytes_storage(uint64 *allocated_bytes); +extern void pgstat_reset_allocated_bytes_storage(void); /* ---------- * Support functions for the SQL-callable functions to @@ -336,5 +349,53 @@ extern PgBackendStatus *pgstat_fetch_stat_beentry(BackendId beid); extern LocalPgBackendStatus *pgstat_fetch_stat_local_beentry(int beid); extern char *pgstat_clip_activity(const char *raw_activity); +/* ---------- + * pgstat_report_allocated_bytes() - + * + * Called to report change in memory allocated for this backend. + * + * my_allocated_bytes initially points to local memory, making it safe to call + * this before pgstats has been initialized. allocation_direction is a + * positive/negative multiplier enum defined above. + * ---------- + */ +static inline void +pgstat_report_allocated_bytes(int64 allocated_bytes, int allocation_direction) +{ + uint64 temp; + + /* + * Avoid *my_allocated_bytes unsigned integer overflow on + * PG_ALLOC_DECREASE + */ + if (allocation_direction == PG_ALLOC_DECREASE && + pg_sub_u64_overflow(*my_allocated_bytes, allocated_bytes, &temp)) + { + *my_allocated_bytes = 0; + ereport(LOG, + errmsg("Backend %d deallocated %lld bytes, exceeding the %llu bytes it is currently reporting allocated. Setting reported to 0.", + MyProcPid, (long long) allocated_bytes, + (unsigned long long) *my_allocated_bytes)); + } + else + *my_allocated_bytes += (allocated_bytes) * allocation_direction; + + return; +} + +/* --------- + * pgstat_zero_my_allocated_bytes() - + * + * Called to zero out local allocated bytes variable after fork to avoid double + * counting allocations. + * --------- + */ +static inline void +pgstat_zero_my_allocated_bytes(void) +{ + *my_allocated_bytes = 0; + + return; +} #endif /* BACKEND_STATUS_H */ diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index e7a2f5856a..ca8eeec7de 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1756,10 +1756,11 @@ pg_stat_activity| SELECT s.datid, s.state, s.backend_xid, s.backend_xmin, + s.allocated_bytes, s.query_id, s.query, s.backend_type - FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) + FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id, allocated_bytes) LEFT JOIN pg_database d ON ((s.datid = d.oid))) LEFT JOIN pg_authid u ON ((s.usesysid = u.oid))); pg_stat_all_indexes| SELECT c.oid AS relid, @@ -1874,7 +1875,7 @@ pg_stat_gssapi| SELECT pid, gss_auth AS gss_authenticated, gss_princ AS principal, gss_enc AS encrypted - FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) + FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id, allocated_bytes) WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, @@ -2055,7 +2056,7 @@ pg_stat_replication| SELECT s.pid, w.sync_priority, w.sync_state, w.reply_time - FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) + FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id, allocated_bytes) JOIN pg_stat_get_wal_senders() w(pid, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag, sync_priority, sync_state, reply_time) ON ((s.pid = w.pid))) LEFT JOIN pg_authid u ON ((s.usesysid = u.oid))); pg_stat_replication_slots| SELECT s.slot_name, @@ -2089,7 +2090,7 @@ pg_stat_ssl| SELECT pid, ssl_client_dn AS client_dn, ssl_client_serial AS client_serial, ssl_issuer_dn AS issuer_dn - FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) + FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id, allocated_bytes) WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out index 1d84407a03..ab7e95c367 100644 --- a/src/test/regress/expected/stats.out +++ b/src/test/regress/expected/stats.out @@ -1126,4 +1126,15 @@ SELECT pg_stat_get_subscription_stats(NULL); (1 row) +-- ensure that allocated_bytes exist for backends +SELECT allocated_bytes > 0 AS result FROM pg_stat_activity WHERE backend_type +IN ('checkpointer', 'background writer', 'walwriter', 'autovacuum launcher'); + result +-------- + t + t + t + t +(4 rows) + -- End of Stats Test diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql index b4d6753c71..2f0b1cc9d8 100644 --- a/src/test/regress/sql/stats.sql +++ b/src/test/regress/sql/stats.sql @@ -535,5 +535,8 @@ SET enable_seqscan TO on; SELECT pg_stat_get_replication_slot(NULL); SELECT pg_stat_get_subscription_stats(NULL); +-- ensure that allocated_bytes exist for backends +SELECT allocated_bytes > 0 AS result FROM pg_stat_activity WHERE backend_type +IN ('checkpointer', 'background writer', 'walwriter', 'autovacuum launcher'); -- End of Stats Test -- 2.25.1