On Wed, Jul 24, 2024 at 2:42 PM John Naylor <johncnaylo...@gmail.com> wrote: > As for lowering the limit, we've experimented with 256kB here: > > https://www.postgresql.org/message-id/canwcazzutvz3lsypauyqvzcezxz7qe+9ntnhgyzdtwxpul+...@mail.gmail.com > > As I mention there, going lower than that would need a small amount of > reorganization in the radix tree. Not difficult -- the thing I'm > concerned about is that we'd likely need to document a separate > minimum for DSA, since that behaves strangely with 256kB and might not > work at all lower than that.
For experimentation, here's a rough patch (really two, squashed together for now) that allows m_w_m to go down to 64kB. drop table if exists test; create table test (a int) with (autovacuum_enabled=false, fillfactor=10); insert into test (a) select i from generate_series(1,2000) i; create index on test (a); update test set a = a + 1; set maintenance_work_mem = '64kB'; vacuum (verbose) test; INFO: vacuuming "john.public.test" INFO: finished vacuuming "john.public.test": index scans: 3 pages: 0 removed, 91 remain, 91 scanned (100.00% of total) The advantage with this is that we don't need to care about MEMORY_CONTEXT_CHECKING or 32/64 bit-ness, since allocating a single large node will immediately blow the limit, and that will happen fairly quickly regardless. I suspect going this low will not work with dynamic shared memory and if so would need a warning comment.
From 25ccfb9ed812c72194bd585906f9f11e18400422 Mon Sep 17 00:00:00 2001 From: John Naylor <john.nay...@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