v1-0001-typcache-Use-new-LAZY_INIT-system.patch

application/octet-stream

Filename: v1-0001-typcache-Use-new-LAZY_INIT-system.patch
Type: application/octet-stream
Part: 1
Message: Re: Unexpected behavior after OOM errors

Patch

Format: format-patch
Series: patch v1-0001
Subject: typcache: Use new LAZY_INIT system
File+
src/backend/utils/cache/typcache.c 37 16
src/include/miscadmin.h 39 0
From df023ee26492199e0a120416ebdcc1e3af1d4c76 Mon Sep 17 00:00:00 2001
From: Matthias van de Meent <boekewurm+postgres@gmail.com>
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)