0003-WIP-resizable-shared-memory-structures-20260217.patch
text/x-patch
Filename: 0003-WIP-resizable-shared-memory-structures-20260217.patch
Type: text/x-patch
Part: 2
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch 0003
Subject: WIP: resizable shared memory structures
| File | + | − |
|---|---|---|
| doc/src/sgml/system-views.sgml | 10 | 0 |
| src/backend/port/sysv_shmem.c | 44 | 0 |
| src/backend/port/win32_shmem.c | 36 | 0 |
| src/backend/storage/ipc/ipci.c | 3 | 0 |
| src/backend/storage/ipc/shmem.c | 190 | 18 |
| src/include/catalog/pg_proc.dat | 2 | 2 |
| src/include/storage/pg_shmem.h | 2 | 0 |
| src/include/storage/shmem.h | 13 | 0 |
| src/test/modules/Makefile | 1 | 0 |
| src/test/modules/meson.build | 1 | 0 |
| src/test/modules/resizable_shmem/expected/resizable_shmem.out | 149 | 0 |
| src/test/modules/resizable_shmem/Makefile | 23 | 0 |
| src/test/modules/resizable_shmem/meson.build | 37 | 0 |
| src/test/modules/resizable_shmem/resizable_shmem--1.0.sql | 39 | 0 |
| src/test/modules/resizable_shmem/resizable_shmem.c | 270 | 0 |
| src/test/modules/resizable_shmem/resizable_shmem.conf | 4 | 0 |
| src/test/modules/resizable_shmem/resizable_shmem.control | 5 | 0 |
| src/test/modules/resizable_shmem/specs/resizable_shmem.spec | 42 | 0 |
| src/test/regress/expected/rules.out | 3 | 2 |
From 68dfc9c94e32a639102bfc7f0694636fb82134c3 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Date: Tue, 17 Feb 2026 16:51:20 +0530
Subject: [PATCH 3/3] WIP: resizable shared memory structures
---
doc/src/sgml/system-views.sgml | 10 +
src/backend/port/sysv_shmem.c | 44 +++
src/backend/port/win32_shmem.c | 36 +++
src/backend/storage/ipc/ipci.c | 3 +
src/backend/storage/ipc/shmem.c | 208 ++++++++++++--
src/include/catalog/pg_proc.dat | 4 +-
src/include/storage/pg_shmem.h | 2 +
src/include/storage/shmem.h | 13 +
src/test/modules/Makefile | 1 +
src/test/modules/meson.build | 1 +
src/test/modules/resizable_shmem/Makefile | 23 ++
.../expected/resizable_shmem.out | 149 ++++++++++
src/test/modules/resizable_shmem/meson.build | 37 +++
.../resizable_shmem/resizable_shmem--1.0.sql | 39 +++
.../modules/resizable_shmem/resizable_shmem.c | 270 ++++++++++++++++++
.../resizable_shmem/resizable_shmem.conf | 4 +
.../resizable_shmem/resizable_shmem.control | 5 +
.../specs/resizable_shmem.spec | 42 +++
src/test/regress/expected/rules.out | 5 +-
19 files changed, 874 insertions(+), 22 deletions(-)
create mode 100644 src/test/modules/resizable_shmem/Makefile
create mode 100644 src/test/modules/resizable_shmem/expected/resizable_shmem.out
create mode 100644 src/test/modules/resizable_shmem/meson.build
create mode 100644 src/test/modules/resizable_shmem/resizable_shmem--1.0.sql
create mode 100644 src/test/modules/resizable_shmem/resizable_shmem.c
create mode 100644 src/test/modules/resizable_shmem/resizable_shmem.conf
create mode 100644 src/test/modules/resizable_shmem/resizable_shmem.control
create mode 100644 src/test/modules/resizable_shmem/specs/resizable_shmem.spec
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index 8b4abef8c68..881c0ffb360 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -4247,6 +4247,16 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
the columns will be equal in that case also.
</para></entry>
</row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>reserved_size</structfield> <type>int8</type>
+ </para>
+ <para>
+ Maximum size in bytes that the allocation can grow upto including padding
+ in case of resizable allocations.
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 2e3886cf9fe..32a4d5ed428 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -589,6 +589,27 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
return true;
}
+/*
+ * Get the page size of being used by the shared memory.
+ *
+ * The function should be called only after the shared memory has been setup.
+ */
+Size
+GetOSPageSize(void)
+{
+ Size os_page_size;
+
+ Assert(huge_pages_status != HUGE_PAGES_UNKNOWN);
+
+ os_page_size = sysconf(_SC_PAGESIZE);
+
+ /* If huge pages are actually in use, use huge page size */
+ if (huge_pages_status == HUGE_PAGES_ON)
+ GetHugePageSize(&os_page_size, NULL);
+
+ return os_page_size;
+}
+
/*
* Creates an anonymous mmap()ed shared memory segment.
*
@@ -991,3 +1012,26 @@ PGSharedMemoryDetach(void)
AnonymousShmem = NULL;
}
}
+
+/*
+ * Release part of the shared memory of given size starting at given address.
+ *
+ * The address and size are expected to be page aligned.
+ *
+ * Only supported on platforms that support anonymous shared memory.
+ */
+void PGSharedMemoryRelease(void *addr, Size size)
+{
+ if (!AnonymousShmem)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("releasing shared memory is not supported with the current \"shared_memory_type\" setting")));
+
+ Assert(addr == (void *) TYPEALIGN(GetOSPageSize(), addr));
+ Assert(size == TYPEALIGN(GetOSPageSize(), size));
+ Assert(size > 0);
+
+ if (madvise(addr, size, MADV_REMOVE) == -1)
+ ereport(ERROR,
+ (errmsg("could not release shared memory: %m")));
+}
diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c
index 794e4fcb2ad..394bb8e6282 100644
--- a/src/backend/port/win32_shmem.c
+++ b/src/backend/port/win32_shmem.c
@@ -621,6 +621,19 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild)
return true;
}
+/*
+ * Release part of the shared memory of given size from given address.
+ *
+ * Not supported on Windows currently.
+ */
+void
+PGSharedMemoryRelease(void *addr, Size size)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("releasing part of shared memory is not supported on windows")));
+}
+
/*
* This function is provided for consistency with sysv_shmem.c and does not
* provide any useful information for Windows. To obtain the large page size,
@@ -648,3 +661,26 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
}
return true;
}
+
+/*
+ * Get the page size used by the shared memory.
+ *
+ * The function should be called only after the shared memory has been setup.
+ */
+Size
+GetOSPageSize(void)
+{
+ SYSTEM_INFO sysinfo;
+ Size os_page_size;
+
+ Assert(huge_pages_status != HUGE_PAGES_UNKNOWN);
+
+ GetSystemInfo(&sysinfo);
+ os_page_size = sysinfo.dwPageSize;
+
+ /* If huge pages are actually in use, use huge page size */
+ if (huge_pages_status == HUGE_PAGES_ON)
+ GetHugePageSize(&os_page_size, NULL);
+
+ return os_page_size;
+}
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 952988645d0..410bbfaf678 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -360,6 +360,9 @@ InitializeShmemGUCs(void)
/*
* Calculate the shared memory size and round up to the nearest megabyte.
*/
+ /*
+ * TODO: we need to do a better job of separating out maximum shared memory
+ * and minimum shared memory because of on-demand shared memory segments. */
size_b = CalculateShmemSize();
size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
sprintf(buf, "%zu", size_mb);
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index e73ac489b2b..31bd28c84fe 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -143,6 +143,9 @@ ShmemRegisterStruct(ShmemStructDesc *desc)
elog(DEBUG2, "REGISTER: %s with size %zd", desc->name, desc->size);
registry[num_registrations++] = desc;
+
+ if (desc->max_size > 0)
+ elog(DEBUG2, "RESIZABLE structure: %s has max_size %zd", desc->name, desc->max_size);
}
size_t
@@ -153,7 +156,7 @@ ShmemRegisteredSize(void)
size = 0;
for (int i = 0; i < num_registrations; i++)
{
- size = add_size(size, registry[i]->size);
+ size = add_size(size, registry[i]->max_size > 0 ? registry[i]->max_size : registry[i]->size);
size = add_size(size, registry[i]->extra_size);
}
@@ -162,9 +165,27 @@ ShmemRegisteredSize(void)
return size;
}
+/*
+ * Allocate memory for the registered shared structures and initialize them.
+ *
+ * A shared structure may be fixed sized, with max_size = 0 or resizable, with
+ * max_size > 0. A resizable structure which does not span multiple pages is
+ * treated as a fixed size structure since the memory gets allocated or
+ * deallocated only in pages. The function allocates all the fixed sized
+ * structures first to pack them tightly. To easily deallocated memory in pages,
+ * the resizable structures are allocated on page boundaries. Unregistered
+ * objects are allocated after the registered structures and are treated as fixed
+ * structures.
+ *
+ * Because registered structures are allocated in pages instead of bytes, the
+ * actual allocated size may be more than the requested size. This may leave less
+ * space for unregistered structures than expected.
+ */
void
ShmemInitRegistered(void)
{
+ Size page_size = GetOSPageSize();
+
/* Should be called only by the postmaster or a standalone backend. */
Assert(!IsUnderPostmaster);
@@ -177,6 +198,27 @@ ShmemInitRegistered(void)
elog(DEBUG2, "INIT [%d/%d]: %s", i, num_registrations, registry[i]->name);
+ /*
+ * Pack fixed sized structures and resizable structures that do not
+ * span multiple pages tightly together.
+ */
+ if (registry[i]->max_size > 0)
+ {
+ Size pages_for_max_size;
+
+ pages_for_max_size = (registry[i]->max_size + page_size - 1) / page_size;
+
+ if (pages_for_max_size <= 1)
+ {
+ elog(DEBUG2, "RESIZABLE structure: %s does not span across pages, treating it as fixed size structure with size %zd",
+ registry[i]->name, registry[i]->max_size);
+ registry[i]->size = registry[i]->max_size;
+ registry[i]->max_size = 0;
+ }
+ else
+ continue;
+ }
+
/* look it up in the shmem index */
result = (ShmemIndexEnt *)
hash_search(ShmemIndex, registry[i]->name, HASH_ENTER_NULL, &found);
@@ -204,14 +246,132 @@ ShmemInitRegistered(void)
}
result->size = registry[i]->size;
result->allocated_size = allocated_size;
+ result->reservedsize = allocated_size;
result->location = structPtr;
*(registry[i]->ptr) = structPtr;
if (registry[i]->init_fn)
registry[i]->init_fn(registry[i]->init_fn_arg);
}
+
+ /* Allocate resizable structures on page boundaries. */
+ ShmemAllocator->free_offset = align_size(ShmemAllocator->free_offset, page_size);
+ for (int i = 0; i < num_registrations; i++)
+ {
+ size_t allocated_size;
+ void *structPtr;
+ bool found;
+ ShmemIndexEnt *result;
+
+ /*
+ * Handle truely resizable structures.
+ */
+ if (registry[i]->max_size > 0)
+ {
+#ifdef USE_ASSERT_CHECKING
+ Size pages_for_max_size;
+
+ pages_for_max_size = (registry[i]->max_size + page_size - 1) / page_size;
+
+ Assert (pages_for_max_size > 1);
+#endif
+ registry[i]->max_size = align_size(registry[i]->max_size, page_size);
+ elog(DEBUG2, "RESIZABLE structure: %s with maximum size %zd",
+ registry[i]->name, registry[i]->max_size);
+ }
+ else
+ continue;
+
+ /* look it up in the shmem index */
+ result = (ShmemIndexEnt *)
+ hash_search(ShmemIndex, registry[i]->name, HASH_ENTER_NULL, &found);
+ if (!result)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("could not create ShmemIndex entry for data structure \"%s\"",
+ registry[i]->name)));
+ }
+ if (found)
+ elog(ERROR, "shmem struct \"%s\" is already initialized", registry[i]->name);
+
+ /*
+ * Allocate maximum size for a resizable structure. This should not
+ * allocate memory but just the address space. Memory will be allocated
+ * as the address space is written to. It is expected that the
+ * registrants do not use memory beyond the initial size until they have
+ * resized the structure. If they do so, this will result in allocating
+ * more memory than expected.
+ */
+ structPtr = ShmemAllocRaw(registry[i]->max_size, &allocated_size);
+ if (structPtr == NULL)
+ {
+ /* out of memory; remove the failed ShmemIndex entry */
+ hash_search(ShmemIndex, registry[i]->name, HASH_REMOVE, NULL);
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("not enough shared memory for data structure"
+ " \"%s\" (%zu bytes requested)",
+ registry[i]->name, registry[i]->size)));
+ }
+ /* Since max_size is page aligned, it should also be cachealigned. */
+ Assert(allocated_size == registry[i]->max_size);
+ result->size = registry[i]->size;
+ /* Resizable structure memory will get allocated in pages. */
+ result->allocated_size = align_size(registry[i]->size, page_size);
+ result->reservedsize = allocated_size;
+ result->location = structPtr;
+
+ *(registry[i]->ptr) = structPtr;
+ if (registry[i]->init_fn)
+ registry[i]->init_fn(registry[i]->init_fn_arg);
+ }
+
+ /* All allocations were in page sizes, so free space should be page aligned. */
+ Assert(ShmemAllocator->free_offset == align_size(ShmemAllocator->free_offset, page_size))
}
+#ifndef EXEC_BACKEND
+void
+ShmemResizeRegistered(const char *name, Size new_size)
+{
+ ShmemIndexEnt *result;
+ bool found;
+ Size allocated_size;
+
+ /* look it up in the shmem index */
+ LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
+ result = (ShmemIndexEnt *)
+ hash_search(ShmemIndex, name, HASH_FIND, &found);
+ if (!found)
+ elog(ERROR, "shmem struct \"%s\" is not initialized", name);
+
+ Assert(result);
+
+ /* Resizable structure memory will get allocated in pages. */
+ allocated_size = align_size(new_size, GetOSPageSize());
+ if (result->reservedsize < allocated_size)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
+ errmsg("not enough address space is reserved for resizing structure \"%s\"", name)));
+
+
+ /*
+ * If shrinking release memory which is not required anymore. If expanding we
+ * don't have to do anything as the memory is allocated when it's written to.
+ */
+ if (allocated_size < result->allocated_size)
+ PGSharedMemoryRelease((char *) result->location + allocated_size,
+ result->reservedsize - allocated_size);
+
+ /* Update shmem index entry. */
+ result->size = new_size;
+ result->allocated_size = allocated_size;
+
+ LWLockRelease(ShmemIndexLock);
+}
+#endif
+
#ifdef EXEC_BACKEND
void
ShmemAttachRegistered(void)
@@ -700,6 +860,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
}
result->size = size;
result->allocated_size = allocated_size;
+ result->reservedsize = allocated_size;
result->location = structPtr;
}
@@ -743,11 +904,26 @@ mul_size(Size s1, Size s2)
return result;
}
+/*
+ * Round up the given size to the next multiple of the given alignment, checking
+ * for overflow.
+ */
+Size
+align_size(Size size, Size alignment)
+{
+
+ Assert(alignment != 0);
+
+ if (size % alignment == 0)
+ return size;
+ return add_size(size, alignment - (size % alignment));
+}
+
/* SQL SRF showing allocated shared memory */
Datum
pg_get_shmem_allocations(PG_FUNCTION_ARGS)
{
-#define PG_GET_SHMEM_SIZES_COLS 4
+#define PG_GET_SHMEM_SIZES_COLS 5
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
HASH_SEQ_STATUS hstat;
ShmemIndexEnt *ent;
@@ -769,7 +945,17 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
values[2] = Int64GetDatum(ent->size);
values[3] = Int64GetDatum(ent->allocated_size);
- named_allocated += ent->allocated_size;
+ values[4] = Int64GetDatum(ent->reservedsize);
+
+ /*
+ * The shared memory segment metadata does not know about the internal
+ * reservation. From it's point of view the maximum size of a resizable
+ * shared structure is considered to be allocated. In order to compute
+ * the amount of memory allocated to the unnamed structures, add
+ * reserved size, which is same as the allocated size for a fixed sized
+ * structure.
+ */
+ named_allocated += ent->reservedsize;
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
values, nulls);
@@ -975,23 +1161,9 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
Size
pg_get_shmem_pagesize(void)
{
- Size os_page_size;
-#ifdef WIN32
- SYSTEM_INFO sysinfo;
-
- GetSystemInfo(&sysinfo);
- os_page_size = sysinfo.dwPageSize;
-#else
- os_page_size = sysconf(_SC_PAGESIZE);
-#endif
-
Assert(IsUnderPostmaster);
- Assert(huge_pages_status != HUGE_PAGES_UNKNOWN);
-
- if (huge_pages_status == HUGE_PAGES_ON)
- GetHugePageSize(&os_page_size, NULL);
- return os_page_size;
+ return GetOSPageSize();
}
Datum
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 83f6501df38..dfd3389ced3 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8592,8 +8592,8 @@
{ oid => '5052', descr => 'allocations from the main shared memory segment',
proname => 'pg_get_shmem_allocations', prorows => '50', proretset => 't',
provolatile => 'v', prorettype => 'record', proargtypes => '',
- proallargtypes => '{text,int8,int8,int8}', proargmodes => '{o,o,o,o}',
- proargnames => '{name,off,size,allocated_size}',
+ proallargtypes => '{text,int8,int8,int8,int8}', proargmodes => '{o,o,o,o,o}',
+ proargnames => '{name,off,size,allocated_size,reserved_size}',
prosrc => 'pg_get_shmem_allocations' },
{ oid => '4099', descr => 'Is NUMA support available?',
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 10c7b065861..07e7f1a9661 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -89,6 +89,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size,
PGShmemHeader **shim);
extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
extern void PGSharedMemoryDetach(void);
+extern void PGSharedMemoryRelease(void *addr, Size size);
extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
+extern Size GetOSPageSize(void);
#endif /* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index cbd4ef8d03f..a1fe945e02b 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -50,6 +50,14 @@ typedef struct ShmemStructDesc
*/
size_t extra_size;
+ /*
+ * Maximum size this structure can grow upto in future. The memory is not
+ * allocated right away but the corresponding address space is allocated so
+ * that memory can be mapped to it when the structure grows. Typically
+ * should be used for structures which need contiguous memory.
+ */
+ size_t max_size;
+
/* Pointer to the variable to which pointer to this shared memory area is assigned after allocation. */
void **ptr;
} ShmemStructDesc;
@@ -84,6 +92,9 @@ extern void InitShmemIndex(void);
extern void ShmemRegisterHash(ShmemHashDesc *desc, HASHCTL *infoP, int hash_flags);
extern void ShmemRegisterStruct(ShmemStructDesc *desc);
+#ifndef EXEC_BACKEND
+extern void ShmemResizeRegistered(const char *name, Size new_size);
+#endif
/* Legacy functions */
extern HTAB *ShmemInitHash(const char *name, int64 init_size, int64 max_size,
@@ -96,6 +107,7 @@ extern void ShmemAttachRegistered(void);
extern Size add_size(Size s1, Size s2);
extern Size mul_size(Size s1, Size s2);
+extern Size align_size(Size size, Size align);
extern PGDLLIMPORT Size pg_get_shmem_pagesize(void);
@@ -115,6 +127,7 @@ typedef struct
void *location; /* location in shared mem */
Size size; /* # bytes requested for the structure */
Size allocated_size; /* # bytes actually allocated */
+ Size reservedsize; /* # bytes reserved for this resizable structure. */
} ShmemIndexEnt;
#endif /* SHMEM_H */
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 44c7163c1cd..a5df6edae18 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -14,6 +14,7 @@ SUBDIRS = \
libpq_pipeline \
oauth_validator \
plsample \
+ resizable_shmem \
spgist_name_ops \
test_aio \
test_binaryheap \
diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build
index 2634a519935..961bb62759d 100644
--- a/src/test/modules/meson.build
+++ b/src/test/modules/meson.build
@@ -13,6 +13,7 @@ subdir('libpq_pipeline')
subdir('nbtree')
subdir('oauth_validator')
subdir('plsample')
+subdir('resizable_shmem')
subdir('spgist_name_ops')
subdir('ssl_passphrase_callback')
subdir('test_aio')
diff --git a/src/test/modules/resizable_shmem/Makefile b/src/test/modules/resizable_shmem/Makefile
new file mode 100644
index 00000000000..ad2f040b2f0
--- /dev/null
+++ b/src/test/modules/resizable_shmem/Makefile
@@ -0,0 +1,23 @@
+# src/test/modules/resizable_shmem/Makefile
+
+MODULES = resizable_shmem
+ISOLATION = resizable_shmem
+
+EXTENSION = resizable_shmem
+DATA = resizable_shmem--1.0.sql
+PGFILEDESC = "resizable_shmem - test module for resizable shared memory"
+
+# This test requires library to be loaded at the server start, so disable
+# installcheck
+NO_INSTALLCHECK = 1
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/resizable_shmem
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/src/makefiles/pgxs.mk
+endif
diff --git a/src/test/modules/resizable_shmem/expected/resizable_shmem.out b/src/test/modules/resizable_shmem/expected/resizable_shmem.out
new file mode 100644
index 00000000000..afb18dce0a8
--- /dev/null
+++ b/src/test/modules/resizable_shmem/expected/resizable_shmem.out
@@ -0,0 +1,149 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1u s2u zu zia s1w_initial s1u s2r_initial s2u zs_max zu zia s2r_initial s2w_max s2u s1r_max s1u zs_shrink zu zia s1r_shrink s1w_shrink s1u s2r_shrink s2u zs_fail
+step s1u: SELECT pg_size_pretty(rss_shmem) AS rss_shmem FROM resizable_shmem_usage() AS "session1_shmem_usage";
+rss_shmem
+---------
+5504 kB
+(1 row)
+
+step s2u: SELECT pg_size_pretty(rss_shmem) AS rss_shmem FROM resizable_shmem_usage() AS "session2_shmem_usage";
+rss_shmem
+---------
+5504 kB
+(1 row)
+
+step zu: SELECT pg_size_pretty(rss_shmem) AS rss_shmem FROM resizable_shmem_usage() AS "resizer_shmem_usage";
+rss_shmem
+---------
+5504 kB
+(1 row)
+
+step zia: SELECT name, pg_size_pretty(size) AS size, pg_size_pretty(allocated_size) AS allocated_size, pg_size_pretty(reserved_size) AS reserved_size FROM pg_shmem_allocations WHERE name = 'resizable_shmem';
+name |size |allocated_size|reserved_size
+---------------+------+--------------+-------------
+resizable_shmem|100 MB|100 MB |400 MB
+(1 row)
+
+step s1w_initial: SELECT resizable_shmem_write(100);
+resizable_shmem_write
+---------------------
+
+(1 row)
+
+step s1u: SELECT pg_size_pretty(rss_shmem) AS rss_shmem FROM resizable_shmem_usage() AS "session1_shmem_usage";
+rss_shmem
+---------
+105 MB
+(1 row)
+
+step s2r_initial: SELECT resizable_shmem_read(25 * 1024 * 1024, 100);
+resizable_shmem_read
+--------------------
+t
+(1 row)
+
+step s2u: SELECT pg_size_pretty(rss_shmem) AS rss_shmem FROM resizable_shmem_usage() AS "session2_shmem_usage";
+rss_shmem
+---------
+105 MB
+(1 row)
+
+step zs_max: SELECT resizable_shmem_resize(100 * 1024 * 1024);
+resizable_shmem_resize
+----------------------
+
+(1 row)
+
+step zu: SELECT pg_size_pretty(rss_shmem) AS rss_shmem FROM resizable_shmem_usage() AS "resizer_shmem_usage";
+rss_shmem
+---------
+6272 kB
+(1 row)
+
+step zia: SELECT name, pg_size_pretty(size) AS size, pg_size_pretty(allocated_size) AS allocated_size, pg_size_pretty(reserved_size) AS reserved_size FROM pg_shmem_allocations WHERE name = 'resizable_shmem';
+name |size |allocated_size|reserved_size
+---------------+------+--------------+-------------
+resizable_shmem|400 MB|400 MB |400 MB
+(1 row)
+
+step s2r_initial: SELECT resizable_shmem_read(25 * 1024 * 1024, 100);
+resizable_shmem_read
+--------------------
+t
+(1 row)
+
+step s2w_max: SELECT resizable_shmem_write(500);
+resizable_shmem_write
+---------------------
+
+(1 row)
+
+step s2u: SELECT pg_size_pretty(rss_shmem) AS rss_shmem FROM resizable_shmem_usage() AS "session2_shmem_usage";
+rss_shmem
+---------
+405 MB
+(1 row)
+
+step s1r_max: SELECT resizable_shmem_read(100 * 1024 * 1024, 500);
+resizable_shmem_read
+--------------------
+t
+(1 row)
+
+step s1u: SELECT pg_size_pretty(rss_shmem) AS rss_shmem FROM resizable_shmem_usage() AS "session1_shmem_usage";
+rss_shmem
+---------
+405 MB
+(1 row)
+
+step zs_shrink: SELECT resizable_shmem_resize(75 * 1024 * 1024);
+resizable_shmem_resize
+----------------------
+
+(1 row)
+
+step zu: SELECT pg_size_pretty(rss_shmem) AS rss_shmem FROM resizable_shmem_usage() AS "resizer_shmem_usage";
+rss_shmem
+---------
+6272 kB
+(1 row)
+
+step zia: SELECT name, pg_size_pretty(size) AS size, pg_size_pretty(allocated_size) AS allocated_size, pg_size_pretty(reserved_size) AS reserved_size FROM pg_shmem_allocations WHERE name = 'resizable_shmem';
+name |size |allocated_size|reserved_size
+---------------+------+--------------+-------------
+resizable_shmem|300 MB|300 MB |400 MB
+(1 row)
+
+step s1r_shrink: SELECT resizable_shmem_read(75 * 1024 * 1024, 500);
+resizable_shmem_read
+--------------------
+t
+(1 row)
+
+step s1w_shrink: SELECT resizable_shmem_write(999);
+resizable_shmem_write
+---------------------
+
+(1 row)
+
+step s1u: SELECT pg_size_pretty(rss_shmem) AS rss_shmem FROM resizable_shmem_usage() AS "session1_shmem_usage";
+rss_shmem
+---------
+305 MB
+(1 row)
+
+step s2r_shrink: SELECT resizable_shmem_read(75 * 1024 * 1024, 999);
+resizable_shmem_read
+--------------------
+t
+(1 row)
+
+step s2u: SELECT pg_size_pretty(rss_shmem) AS rss_shmem FROM resizable_shmem_usage() AS "session2_shmem_usage";
+rss_shmem
+---------
+305 MB
+(1 row)
+
+step zs_fail: SELECT resizable_shmem_resize(128 * 1024 * 1024);
+ERROR: not enough address space is reserved for resizing structure "resizable_shmem"
diff --git a/src/test/modules/resizable_shmem/meson.build b/src/test/modules/resizable_shmem/meson.build
new file mode 100644
index 00000000000..6e5f6d5caaf
--- /dev/null
+++ b/src/test/modules/resizable_shmem/meson.build
@@ -0,0 +1,37 @@
+# src/test/modules/test_resizable_shmem/meson.build
+
+resizable_shmem_sources = files(
+ 'resizable_shmem.c',
+)
+
+if host_system == 'windows'
+ resizable_shmem_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+ '--NAME', 'resizable_shmem',
+ '--FILEDESC', 'resizable_shmem - test module for resizable shared memory',])
+endif
+
+resizable_shmem = shared_module('resizable_shmem',
+ resizable_shmem_sources,
+ kwargs: pg_test_mod_args,
+)
+test_install_libs += resizable_shmem
+
+test_install_data += files(
+ 'resizable_shmem.control',
+ 'resizable_shmem--1.0.sql',
+)
+
+tests += {
+ 'name': 'resizable_shmem',
+ 'sd': meson.current_source_dir(),
+ 'bd': meson.current_build_dir(),
+ 'isolation': {
+ 'specs': [
+ 'resizable_shmem',
+ ],
+ 'regress_args': ['--temp-config', files('resizable_shmem.conf')],
+ # This test requires library to be loaded at the server start, so disable
+ # installcheck
+ 'runningcheck': false,
+ },
+}
diff --git a/src/test/modules/resizable_shmem/resizable_shmem--1.0.sql b/src/test/modules/resizable_shmem/resizable_shmem--1.0.sql
new file mode 100644
index 00000000000..2a5fa957167
--- /dev/null
+++ b/src/test/modules/resizable_shmem/resizable_shmem--1.0.sql
@@ -0,0 +1,39 @@
+/* src/test/modules/resizable_shmem/resizable_shmem--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION resizable_shmem" to load this file. \quit
+
+-- Function to resize the test structure in the shared memory
+CREATE FUNCTION resizable_shmem_resize(new_entries integer)
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+
+-- Function to write data to all entries in the test structure in shared memory
+-- Writing all the entries makes sure that the memory is actually allocated and
+-- mapped to the process, so that we can later measure the memory usage.
+CREATE FUNCTION resizable_shmem_write(entry_value integer)
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+
+-- Function to verify that specified number of initial entries have expected value.
+-- Reading all the entries makes sure that the memory is actually mapped to the
+-- process, so that we can later measure the memory usage.
+CREATE FUNCTION resizable_shmem_read(entry_count integer, entry_value integer)
+RETURNS boolean
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+
+-- Function to report memory usage statistics of the calling backend
+--
+-- TODO: This function is useful to determine that the memory is freed after
+-- shrinking and that we don't allocate all the memory upfront instead of just
+-- reserving the address space. the C implementation of this function relies
+-- heavily on Linux-specific /proc/self/stats and also the sizes that it returns
+-- may not be stable across different machines. Hence we should consider
+-- removing this function and the related tests after we have verified the
+-- resizing works as expected.
+CREATE FUNCTION resizable_shmem_usage(OUT rss_anon bigint, OUT rss_file bigint, OUT rss_shmem bigint, OUT vm_size bigint)
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
diff --git a/src/test/modules/resizable_shmem/resizable_shmem.c b/src/test/modules/resizable_shmem/resizable_shmem.c
new file mode 100644
index 00000000000..55c86b8e1ab
--- /dev/null
+++ b/src/test/modules/resizable_shmem/resizable_shmem.c
@@ -0,0 +1,270 @@
+/* -------------------------------------------------------------------------
+ *
+ * resizable_shmem.c
+ * Test module for PostgreSQL's resizable shared memory functionality
+ *
+ * This module demonstrates and tests the resizable shared memory API
+ * provided by shmem.c/shmem.h.
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "storage/shmem.h"
+#include "storage/spin.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/memutils.h"
+#include "utils/timestamp.h"
+#include "access/htup_details.h"
+
+#include <stdio.h>
+
+PG_MODULE_MAGIC;
+
+/*
+ * Default amount of shared buffers and hence the amount of shared memory
+ * allocated by default is in hundreds of MBs. The memory allocated to the test
+ * structure will be noticeable only when it's in the same order.
+ */
+#define TEST_INITIAL_ENTRIES (25 * 1024 * 1024) /* Initial number of entries (100MB) */
+#define TEST_MAX_ENTRIES (100 * 1024 * 1024) /* Maximum number of entries (400MB, 4x initial) */
+#define TEST_ENTRY_SIZE sizeof(int32) /* Size of each entry */
+
+/*
+ * Resizable test data structure stored in shared memory.
+ *
+ * We do not use any locks. The test performs resizing, reads and writes none of
+ * which are concurrent to keep the code and the test simple.
+ */
+typedef struct TestResizableShmemStruct
+{
+ /* Metadata */
+ int32 num_entries; /* Number of entries that can fit */
+
+ /* Data area - variable size */
+ int32 data[FLEXIBLE_ARRAY_MEMBER];
+} TestResizableShmemStruct;
+
+/* Global pointer to our shared memory structure */
+static TestResizableShmemStruct *resizable_shmem = NULL;
+
+static void resizable_shmem_shmem_init(void *arg);
+
+static ShmemStructDesc testShmemDesc = {
+ .name = "resizable_shmem",
+ .size = offsetof(TestResizableShmemStruct, data) + (TEST_INITIAL_ENTRIES * TEST_ENTRY_SIZE),
+ .max_size = offsetof(TestResizableShmemStruct, data) + (TEST_MAX_ENTRIES * TEST_ENTRY_SIZE),
+ .alignment = MAXIMUM_ALIGNOF,
+ .init_fn = resizable_shmem_shmem_init,
+ .ptr = (void **) &resizable_shmem,
+};
+
+static shmem_request_hook_type prev_shmem_request_hook = NULL;
+
+static void resizable_shmem_request(void);
+
+/* SQL-callable functions */
+PG_FUNCTION_INFO_V1(resizable_shmem_resize);
+PG_FUNCTION_INFO_V1(resizable_shmem_write);
+PG_FUNCTION_INFO_V1(resizable_shmem_read);
+PG_FUNCTION_INFO_V1(resizable_shmem_usage);
+
+/*
+ * Module load callback
+ */
+void
+_PG_init(void)
+{
+ /*
+ * The module needs to be loaded via shared_preload_libraries to register
+ * shared memory structure. But if that's not the case, don't throw an error.
+ * The SQL functions check for existence of the shared memory data structure.
+ */
+ if (!process_shared_preload_libraries_in_progress)
+ return;
+
+#ifdef EXEC_BACKEND
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("resizable_shmem is not supported in EXEC_BACKEND builds")));
+#endif
+
+ /* Install hook to register shared memory structure. */
+ prev_shmem_request_hook = shmem_request_hook;
+ shmem_request_hook = resizable_shmem_request;
+}
+
+/*
+ * Request shared memory resources
+ */
+static void
+resizable_shmem_request(void)
+{
+ if (prev_shmem_request_hook)
+ prev_shmem_request_hook();
+
+ /* Register our resizable shared memory structure */
+ ShmemRegisterStruct(&testShmemDesc);
+}
+
+/*
+ * Initialize shared memory structure
+ */
+static void
+resizable_shmem_shmem_init(void *arg)
+{
+ /*
+ * Shared memory structure should have been allocated with the requested
+ * size. Initialize the metadata.
+ */
+ Assert(resizable_shmem != NULL);
+ resizable_shmem->num_entries = TEST_INITIAL_ENTRIES;
+}
+
+/*
+ * Resize the shared memory structure to accommodate the specified number of
+ * entries.
+ */
+Datum
+resizable_shmem_resize(PG_FUNCTION_ARGS)
+{
+#ifndef EXEC_BACKEND
+ int32 new_entries = PG_GETARG_INT32(0);
+ Size new_size;
+
+ if (!resizable_shmem)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("resizable_shmem is not initialized")));
+
+ new_size = offsetof(TestResizableShmemStruct, data) + (new_entries * TEST_ENTRY_SIZE);
+ ShmemResizeRegistered(testShmemDesc.name, new_size);
+ resizable_shmem->num_entries = new_entries;
+
+ PG_RETURN_VOID();
+#else
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("resizing shared memory is not supported in EXEC_BACKEND builds")));
+#endif
+}
+
+/*
+ * Write the given integer value to all entries in the data array.
+ */
+Datum
+resizable_shmem_write(PG_FUNCTION_ARGS)
+{
+ int32 entry_value = PG_GETARG_INT32(0);
+ int32 i;
+
+ if (!resizable_shmem)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("resizable_shmem is not initialized")));
+
+ /* Write the value to all current entries */
+ for (i = 0; i < resizable_shmem->num_entries; i++)
+ resizable_shmem->data[i] = entry_value;
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * Check whether the first 'entry_count' entries all have the expected 'entry_value'.
+ * Returns true if all match, false otherwise.
+ */
+Datum
+resizable_shmem_read(PG_FUNCTION_ARGS)
+{
+ int32 entry_count = PG_GETARG_INT32(0);
+ int32 entry_value = PG_GETARG_INT32(1);
+ int32 i;
+
+ if (resizable_shmem == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("resizable_shmem is not initialized")));
+
+ /* Validate entry_count */
+ if (entry_count < 0 || entry_count > resizable_shmem->num_entries)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("entry_count %d is out of range (0..%d)", entry_count, resizable_shmem->num_entries)));
+
+ /* Check if first entry_count entries have the expected value */
+ for (i = 0; i < entry_count; i++)
+ {
+ if (resizable_shmem->data[i] != entry_value)
+ PG_RETURN_BOOL(false);
+ }
+
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * Report multiple memory usage statistics of the calling backend process
+ * as reported by the kernel.
+ * Returns RssAnon, RssFile, RssShmem, VmSize from /proc/self/status as a record.
+ *
+ * TODO: See TODO note in SQL definition of this function.
+ */
+Datum
+resizable_shmem_usage(PG_FUNCTION_ARGS)
+{
+ FILE *f;
+ char line[256];
+ int64 rss_anon_kb = -1;
+ int64 rss_file_kb = -1;
+ int64 rss_shmem_kb = -1;
+ int64 vm_size_kb = -1;
+ int found = 0;
+ TupleDesc tupdesc;
+ Datum values[4];
+ bool nulls[4];
+ HeapTuple tuple;
+
+ /* Open /proc/self/status to read memory information */
+ f = fopen("/proc/self/status", "r");
+ if (f == NULL)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open /proc/self/status: %m")));
+
+ /* Look for the memory usage lines */
+ while (fgets(line, sizeof(line), f) != NULL && found < 4)
+ {
+ if (rss_anon_kb == -1 && sscanf(line, "RssAnon: %ld kB", &rss_anon_kb) == 1)
+ found++;
+ else if (rss_file_kb == -1 && sscanf(line, "RssFile: %ld kB", &rss_file_kb) == 1)
+ found++;
+ else if (rss_shmem_kb == -1 && sscanf(line, "RssShmem: %ld kB", &rss_shmem_kb) == 1)
+ found++;
+ else if (vm_size_kb == -1 && sscanf(line, "VmSize: %ld kB", &vm_size_kb) == 1)
+ found++;
+ }
+
+ fclose(f);
+
+ /* Build tuple descriptor for our result type */
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("function returning record called in context "
+ "that cannot accept a record")));
+
+ /* Build the result tuple */
+ values[0] = Int64GetDatum(rss_anon_kb >= 0 ? rss_anon_kb * 1024 : 0);
+ values[1] = Int64GetDatum(rss_file_kb >= 0 ? rss_file_kb * 1024 : 0);
+ values[2] = Int64GetDatum(rss_shmem_kb >= 0 ? rss_shmem_kb * 1024 : 0);
+ values[3] = Int64GetDatum(vm_size_kb >= 0 ? vm_size_kb * 1024 : 0);
+
+ nulls[0] = nulls[1] = nulls[2] = nulls[3] = false;
+
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+ PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
+}
diff --git a/src/test/modules/resizable_shmem/resizable_shmem.conf b/src/test/modules/resizable_shmem/resizable_shmem.conf
new file mode 100644
index 00000000000..764b6357cbb
--- /dev/null
+++ b/src/test/modules/resizable_shmem/resizable_shmem.conf
@@ -0,0 +1,4 @@
+shared_preload_libraries = 'resizable_shmem'
+# Turn huge pages off so that we can test changes in the shared memory smaller
+# than huge page size which may be in GBs.
+huge_pages = off
diff --git a/src/test/modules/resizable_shmem/resizable_shmem.control b/src/test/modules/resizable_shmem/resizable_shmem.control
new file mode 100644
index 00000000000..1ce2c5ea21a
--- /dev/null
+++ b/src/test/modules/resizable_shmem/resizable_shmem.control
@@ -0,0 +1,5 @@
+# resizable_shmem extension test module
+comment = 'test module for testing resizable shared memory structure functionality'
+default_version = '1.0'
+module_pathname = '$libdir/resizable_shmem'
+relocatable = true
diff --git a/src/test/modules/resizable_shmem/specs/resizable_shmem.spec b/src/test/modules/resizable_shmem/specs/resizable_shmem.spec
new file mode 100644
index 00000000000..8b8cf153df8
--- /dev/null
+++ b/src/test/modules/resizable_shmem/specs/resizable_shmem.spec
@@ -0,0 +1,42 @@
+# Test resizable shared memory structure
+#
+# It tests that a resizable shared memory structure can be resized from any
+# backend and that the new sizes are visible to all the backends. It uses
+# isolation test infrastructure so that resizing, reading and writing can be
+# interleaved.
+
+setup
+{
+ CREATE EXTENSION resizable_shmem;
+}
+
+teardown
+{
+ DROP EXTENSION resizable_shmem;
+}
+
+session "session1"
+step s1w_initial { SELECT resizable_shmem_write(100); }
+step s1r_max { SELECT resizable_shmem_read(100 * 1024 * 1024, 500); }
+step s1r_shrink { SELECT resizable_shmem_read(75 * 1024 * 1024, 500); }
+step s1w_shrink { SELECT resizable_shmem_write(999); }
+step s1u { SELECT pg_size_pretty(rss_shmem) AS rss_shmem FROM resizable_shmem_usage() AS "session1_shmem_usage"; }
+
+session "session2"
+step s2r_initial { SELECT resizable_shmem_read(25 * 1024 * 1024, 100); }
+step s2w_max { SELECT resizable_shmem_write(500); }
+step s2r_shrink { SELECT resizable_shmem_read(75 * 1024 * 1024, 999); }
+step s2u { SELECT pg_size_pretty(rss_shmem) AS rss_shmem FROM resizable_shmem_usage() AS "session2_shmem_usage"; }
+
+session "resizer"
+step zs_max { SELECT resizable_shmem_resize(100 * 1024 * 1024); }
+step zs_shrink { SELECT resizable_shmem_resize(75 * 1024 * 1024); }
+step zs_fail { SELECT resizable_shmem_resize(128 * 1024 * 1024); } # this fails (512MB > 400MB max)
+step zia { SELECT name, pg_size_pretty(size) AS size, pg_size_pretty(allocated_size) AS allocated_size, pg_size_pretty(reserved_size) AS reserved_size FROM pg_shmem_allocations WHERE name = 'resizable_shmem'; }
+step zu { SELECT pg_size_pretty(rss_shmem) AS rss_shmem FROM resizable_shmem_usage() AS "resizer_shmem_usage"; }
+
+# Test shrinking and expanding the shared memory, while ensuring that:
+# - All entries can be written to and read from after each resize
+# - Previously written data remains unchanged in entries that survive resizing
+# Now using 100MB initial (25M entries), 400MB max (100M entries), 300MB shrink (75M entries)
+permutation s1u s2u zu zia s1w_initial s1u s2r_initial s2u zs_max zu zia s2r_initial s2w_max s2u s1r_max s1u zs_shrink zu zia s1r_shrink s1w_shrink s1u s2r_shrink s2u zs_fail
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index f4ee2bd7459..a6eaeb49dbc 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1770,8 +1770,9 @@ pg_shadow| SELECT pg_authid.rolname AS usename,
pg_shmem_allocations| SELECT name,
off,
size,
- allocated_size
- FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size);
+ allocated_size,
+ reserved_size
+ FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size, reserved_size);
pg_shmem_allocations_numa| SELECT name,
numa_node,
size
--
2.34.1