From df023ee26492199e0a120416ebdcc1e3af1d4c76 Mon Sep 17 00:00:00 2001 From: Matthias van de Meent Date: Wed, 17 Jun 2026 14:13:17 +0200 Subject: [PATCH v1 1/2] typcache: Use new LAZY_INIT system This allows us to recover from OOMs when they happen during the initialization of the typcache. --- src/backend/utils/cache/typcache.c | 53 +++++++++++++++++++++--------- src/include/miscadmin.h | 39 ++++++++++++++++++++++ 2 files changed, 76 insertions(+), 16 deletions(-) diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c index da91a2ff1dd..997f0e1cb1c 100644 --- a/src/backend/utils/cache/typcache.c +++ b/src/backend/utils/cache/typcache.c @@ -73,6 +73,7 @@ #include "utils/rel.h" #include "utils/syscache.h" #include "utils/typcache.h" +#include "miscadmin.h" /* The main type cache hashtable searched by lookup_type_cache */ @@ -388,11 +389,12 @@ type_cache_syshash(const void *key, Size keysize) TypeCacheEntry * lookup_type_cache(Oid type_id, int flags) { + LAZY_INIT_SYSTEM(typcache, 5); TypeCacheEntry *typentry; bool found; int in_progress_offset; - if (TypeCacheHash == NULL) + if (!LAZY_INIT_DONE(typcache)) { /* First time through: initialize the hash table */ HASHCTL ctl; @@ -408,34 +410,53 @@ lookup_type_cache(Oid type_id, int flags) */ ctl.hash = type_cache_syshash; - TypeCacheHash = hash_create("Type information cache", 64, - &ctl, HASH_ELEM | HASH_FUNCTION); + LAZY_INIT_STEP(typcache, { + TypeCacheHash = hash_create("Type information cache", 64, + &ctl, HASH_ELEM | HASH_FUNCTION); + }); Assert(RelIdToTypeIdCacheHash == NULL); ctl.keysize = sizeof(Oid); ctl.entrysize = sizeof(RelIdToTypeIdCacheEntry); - RelIdToTypeIdCacheHash = hash_create("Map from relid to OID of cached composite type", 64, - &ctl, HASH_ELEM | HASH_BLOBS); - /* Also set up callbacks for SI invalidations */ - CacheRegisterRelcacheCallback(TypeCacheRelCallback, (Datum) 0); - CacheRegisterSyscacheCallback(TYPEOID, TypeCacheTypCallback, (Datum) 0); - CacheRegisterSyscacheCallback(CLAOID, TypeCacheOpcCallback, (Datum) 0); - CacheRegisterSyscacheCallback(CONSTROID, TypeCacheConstrCallback, (Datum) 0); + LAZY_INIT_STEP(typcache, { + RelIdToTypeIdCacheHash = hash_create("Map from relid to OID of cached composite type", 64, + &ctl, HASH_ELEM | HASH_BLOBS); + }); + + /* + * Also set up callbacks for SI invalidations. + * A critical section is used, because we must not run out of slots + * for syscache callbacks; this backend cannot recover from that. + */ + LAZY_INIT_STEP(typcache, { + START_CRIT_SECTION(); + CacheRegisterRelcacheCallback(TypeCacheRelCallback, (Datum) 0); + CacheRegisterSyscacheCallback(TYPEOID, TypeCacheTypCallback, (Datum) 0); + CacheRegisterSyscacheCallback(CLAOID, TypeCacheOpcCallback, (Datum) 0); + CacheRegisterSyscacheCallback(CONSTROID, TypeCacheConstrCallback, (Datum) 0); + END_CRIT_SECTION(); + }); /* Also make sure CacheMemoryContext exists */ - if (!CacheMemoryContext) - CreateCacheMemoryContext(); + LAZY_INIT_STEP(typcache, { + if (!CacheMemoryContext) + CreateCacheMemoryContext(); + }); /* * reserve enough in_progress_list slots for many cases */ allocsize = 4; - in_progress_list = - MemoryContextAlloc(CacheMemoryContext, - allocsize * sizeof(*in_progress_list)); - in_progress_list_maxlen = allocsize; + LAZY_INIT_STEP(typcache, { + in_progress_list = + MemoryContextAlloc(CacheMemoryContext, + allocsize * sizeof(*in_progress_list)); + in_progress_list_maxlen = allocsize; + }); + + Assert(LAZY_INIT_DONE(typcache)); } Assert(TypeCacheHash != NULL && RelIdToTypeIdCacheHash != NULL); diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 7de0a115402..5c4392a3da3 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -158,6 +158,45 @@ do { \ } while(0) +/* + * Helpers to allow subsystems to run lazy initialization code that can deal + * with temporary resource constraints, like OOMs, without significant manual + * coding to support errors in every call to a subcomponent. + * + * The user is expected to run one _STEP per fallible resource allocation, or + * wrap the code that should be executed just once per session in a critical + * section. + * + * Usage: + * + * LAZY_INIT_SYSTEM(name, # of _STEP invocations for this system); + * if (!LAZY_INIT_DONE(name)) + * { + * ...locals; + * ...infallible code; + * LAZY_INIT_STEP(name, { code }); + * ... + * Assert(LAZY_INIT_DONE(name)); + * } + */ + +#define LAZY_INIT_SYSTEM(sysname, nsteps) \ + static const int LI_STEPS_TOTAL_ ## sysname = (nsteps); \ + static int LI_STEPS_DONE_ ## sysname = 0; \ + int LI_CURRENT_STEP_ ## sysname = 0 + +#define LAZY_INIT_STEP(sysname, operation) \ + do { \ + if ((LI_STEPS_DONE_ ## sysname) == (LI_CURRENT_STEP_ ## sysname)++) \ + { \ + operation; \ + (LI_STEPS_DONE_ ## sysname) += 1; \ + } \ + } while (false) + +#define LAZY_INIT_DONE(sysname) \ + ((LI_STEPS_DONE_ ## sysname) == (LI_STEPS_TOTAL_ ## sysname)) + /***************************************************************************** * globals.h -- * *****************************************************************************/ -- 2.50.1 (Apple Git-155)