From fba2dfb4f4068229e923d8b2d9b1ba62b3e1be11 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Mon, 30 Mar 2026 18:21:04 +0300 Subject: [PATCH v2 5/8] Prevent shared memory hash tables from growing beyond initial size Set HASH_FIXED_SIZE on all shared memory hash tables, to prevent them from growing after the initial allocation. It was always weirdly indeterministic that if you e.g. took a lot of locks, consuming all the unused shared memory for the lock table, you couldn't use that space for other things anymore, but if you used it for other things first, then it was no longer available for locks. Better to be strict and predictable. Increase SHMEM_INDEX_SIZE because we were already above the old value, and it's now a hard limit. XXX Many callers of ShmemInitHash() still pass HASH_FIXED_SIZE, but that's now unnecessary because ShmemInitHash() will set it anyway. I didn't go clean them up yet. Reviewed-by: Tomas Vondra Discussion: https://www.postgresql.org/message-id/01ab1d41-3eda-4705-8bbd-af898f5007f1@iki.fi --- src/backend/storage/ipc/shmem.c | 20 ++++++++------------ src/include/storage/shmem.h | 4 ++-- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c index fd2c7c4590a..47065bb3603 100644 --- a/src/backend/storage/ipc/shmem.c +++ b/src/backend/storage/ipc/shmem.c @@ -24,9 +24,8 @@ * available to POSTGRES: fixed-size structures, queues and hash * tables. Fixed-size structures contain things like global variables * for a module and should never be allocated after the shared memory - * initialization phase. Hash tables have a fixed maximum size, but - * their actual size can vary dynamically. When entries are added - * to the table, more space is allocated. Queues link data structures + * initialization phase. Hash tables have a fixed maximum size and + * cannot grow beyond that. Queues link data structures * that have been allocated either within fixed-size structures or as hash * buckets. Each shared data structure has a string name to identify * it (assigned in the module that declares it). @@ -56,11 +55,7 @@ * * (d) memory allocation model: shared memory can never be * freed, once allocated. Each hash table has its own free list, - * so hash buckets can be reused when an item is deleted. However, - * if one hash table grows very large and then shrinks, its space - * cannot be redistributed to other tables. We could build a simple - * hash bucket garbage collector if need be. Right now, it seems - * unnecessary. + * so hash buckets can be reused when an item is deleted. */ #include "postgres.h" @@ -187,7 +182,7 @@ InitShmemAllocator(PGShmemHeader *seghdr) info.dsize = info.max_dsize = hash_select_dirsize(SHMEM_INDEX_SIZE); info.alloc = ShmemHashAlloc; info.alloc_arg = NULL; - hash_flags = HASH_ELEM | HASH_STRINGS | HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE; + hash_flags = HASH_ELEM | HASH_STRINGS | HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE | HASH_FIXED_SIZE; if (!IsUnderPostmaster) { size = hash_get_shared_size(&info, hash_flags); @@ -318,7 +313,7 @@ ShmemAddrIsValid(const void *addr) * *infoP and hash_flags must specify at least the entry sizes and key * comparison semantics (see hash_create()). Flag bits and values specific * to shared-memory hash tables are added here, except that callers may - * choose to specify HASH_PARTITION and/or HASH_FIXED_SIZE. + * choose to specify HASH_PARTITION. * * Note: before Postgres 9.0, this function returned NULL for some failure * cases. Now, it always throws error instead, so callers need not check @@ -336,14 +331,15 @@ ShmemInitHash(const char *name, /* table string name for shmem index */ /* * Hash tables allocated in shared memory have a fixed directory; it can't * grow or other backends wouldn't be able to find it. So, make sure we - * make it big enough to start with. + * make it big enough to start with. We also allocate all the buckets + * upfront, so hash tables cannot grow later. * * The shared memory allocator must be specified too. */ infoP->dsize = infoP->max_dsize = hash_select_dirsize(nelems); infoP->alloc = ShmemHashAlloc; infoP->alloc_arg = NULL; - hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE; + hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE | HASH_FIXED_SIZE; /* look it up in the shmem index */ location = ShmemInitStruct(name, diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h index 0ae609aca8b..2fccbdb534c 100644 --- a/src/include/storage/shmem.h +++ b/src/include/storage/shmem.h @@ -46,8 +46,8 @@ extern void RequestAddinShmemSpace(Size size); /* size constants for the shmem index table */ /* max size of data structure string name */ #define SHMEM_INDEX_KEYSIZE (48) - /* estimated size of the shmem index table (not a hard limit) */ -#define SHMEM_INDEX_SIZE (64) + /* max number of named shmem structures and hash tables */ +#define SHMEM_INDEX_SIZE (256) /* this is a hash bucket in the shmem index table */ typedef struct -- 2.47.3