From 25ccfb9ed812c72194bd585906f9f11e18400422 Mon Sep 17 00:00:00 2001
From: John Naylor <john.naylor@postgresql.org>
Date: Wed, 24 Jul 2024 18:41:51 +0700
Subject: [PATCH v1] WIP: set minimum maintenance_work_mem to 64kB, matching
 work_mem

To allow this to work for vacuum, create radix tree node
contexts lazily.
---
 src/backend/utils/misc/guc_tables.c |  2 +-
 src/include/lib/radixtree.h         | 44 +++++++++++++++++++++--------
 2 files changed, 34 insertions(+), 12 deletions(-)

diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index f6fcdebb03..853ad8dba1 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -2445,7 +2445,7 @@ struct config_int ConfigureNamesInt[] =
 			GUC_UNIT_KB
 		},
 		&maintenance_work_mem,
-		65536, 1024, MAX_KILOBYTES,
+		65536, 64, MAX_KILOBYTES,
 		NULL, NULL, NULL
 	},
 
diff --git a/src/include/lib/radixtree.h b/src/include/lib/radixtree.h
index 88bf695e3f..bd83b670dd 100644
--- a/src/include/lib/radixtree.h
+++ b/src/include/lib/radixtree.h
@@ -834,12 +834,30 @@ RT_ALLOC_NODE(RT_RADIX_TREE * tree, const uint8 kind, const RT_SIZE_CLASS size_c
 	RT_CHILD_PTR allocnode;
 	RT_NODE    *node;
 	size_t		allocsize;
+	RT_SIZE_CLASS_ELEM class_info;
 
-	allocsize = RT_SIZE_CLASS_INFO[size_class].allocsize;
+	class_info = RT_SIZE_CLASS_INFO[size_class];
+	allocsize = class_info.allocsize;
 
 #ifdef RT_SHMEM
 	allocnode.alloc = dsa_allocate(tree->dsa, allocsize);
 #else
+
+	/*
+	 * The context for RT_CLASS_4 should have been created when the tree was
+	 * initialized.
+	 */
+	Assert(tree->node_slabs[RT_CLASS_4] != NULL);
+		if (size_class > RT_CLASS_4 && tree->node_slabs[size_class] == NULL)
+	{
+		size_t		blocksize = RT_SLAB_BLOCK_SIZE(allocsize);
+
+		tree->node_slabs[size_class] = SlabContextCreate(tree->context,
+														 class_info.name,
+														 blocksize,
+														 allocsize);
+	}
+
 	allocnode.alloc = (RT_PTR_ALLOC) MemoryContextAlloc(tree->node_slabs[size_class],
 														allocsize);
 #endif
@@ -1827,6 +1845,9 @@ RT_CREATE(MemoryContext ctx)
 	RT_CHILD_PTR rootnode;
 #ifdef RT_SHMEM
 	dsa_pointer dp;
+#else
+	RT_SIZE_CLASS_ELEM class_info;
+	size_t		node_blocksize;
 #endif
 
 	old_ctx = MemoryContextSwitchTo(ctx);
@@ -1852,17 +1873,18 @@ RT_CREATE(MemoryContext ctx)
 #else
 	tree->ctl = (RT_RADIX_TREE_CONTROL *) palloc0(sizeof(RT_RADIX_TREE_CONTROL));
 
-	/* Create a slab context for each size class */
-	for (int i = 0; i < RT_NUM_SIZE_CLASSES; i++)
-	{
-		RT_SIZE_CLASS_ELEM size_class = RT_SIZE_CLASS_INFO[i];
-		size_t		inner_blocksize = RT_SLAB_BLOCK_SIZE(size_class.allocsize);
+	/*
+	 * For now only create a slab context for the smallest size class. The
+	 * rest will be created on demand. This allows us to stay within memory
+	 * settings that are smaller than the size of the larger slab blocks.
+	 */
+	class_info = RT_SIZE_CLASS_INFO[RT_CLASS_4];
+	node_blocksize = RT_SLAB_BLOCK_SIZE(class_info.allocsize);
 
-		tree->node_slabs[i] = SlabContextCreate(ctx,
-												size_class.name,
-												inner_blocksize,
-												size_class.allocsize);
-	}
+	tree->node_slabs[RT_CLASS_4] = SlabContextCreate(ctx,
+													 class_info.name,
+													 node_blocksize,
+													 class_info.allocsize);
 
 	/* By default we use the passed context for leaves. */
 	tree->leaf_context = tree->context;
-- 
2.45.2

