Re: Better shared data structure management and resizable shared data structures
Heikki Linnakangas <hlinnaka@iki.fi>
Attachments
- v11-0001-refactor-Move-ShmemInitHash-to-separate-file.patch (text/x-patch) patch v11-0001
- v11-0002-Introduce-a-new-mechanism-for-registering-shared.patch (text/x-patch) patch v11-0002
- v11-0003-Add-test-module-to-test-after-startup-shmem-allo.patch (text/x-patch) patch v11-0003
- v11-0004-Convert-pg_stat_statements-to-use-the-new-interf.patch (text/x-patch) patch v11-0004
- v11-0005-Introduce-registry-of-built-in-subsystems.patch (text/x-patch) patch v11-0005
- v11-0006-Convert-lwlock.c-to-use-the-new-interface.patch (text/x-patch) patch v11-0006
- v11-0007-Use-the-new-mechanism-in-a-few-core-subsystems.patch (text/x-patch) patch v11-0007
- v11-0008-refactor-predicate.c-inline-SerialInit-to-the-ca.patch (text/x-patch) patch v11-0008
- v11-0009-refactor-predicate.c-Move-all-the-initialization.patch (text/x-patch) patch v11-0009
- v11-0010-Convert-SLRUs-to-use-the-new-interface.patch (text/x-patch) patch v11-0010
- v11-0011-Convert-AIO-to-the-new-interface.patch (text/x-patch) patch v11-0011
- v11-0012-Add-option-for-aligning-shmem-allocations.patch (text/x-patch) patch v11-0012
- v11-0013-Convert-buffer-manager-to-the-new-API.patch (text/x-patch) patch v11-0013
- v11-0014-Convert-all-remaining-subsystems-to-use-the-new-.patch (text/x-patch) patch v11-0014
On 04/04/2026 15:00, Matthias van de Meent wrote:
> On Sat, 4 Apr 2026 at 02:45, Heikki Linnakangas <hlinnaka@iki.fi> wrote:
>>>> I don't understand the use of ShmemStructDesc. They generally/always
>>>> are private to request_fn(), and their fields are used exclusively
>>>> inside the shmem mechanisms, with no reads of its fields that can't
>>>> already be deduced from context. Why do we need that struct
>>>> everywhere?
>>>
>>> My resizable shared memory structure patches use it as a handle to the
>>> structure to be resized.
>>
>> Right. And hash tables and SLRUs use a desc-like object already, so for
>> symmetry it feels natural to have it for plain structs too.
>> I wonder if we should make it optional though, for the common case that
>> you have no intention of doing anything more with the shmem region that
>> you'd need a desc for. I'm thinking you could just pass NULL for the
>> desc pointer:
>>
>> ShmemRequestStruct(NULL,
>> .name = "pg_stat_statements",
>> .size = sizeof(pgssSharedState),
>> .ptr = (void **) &pgss,
>> };
>
> That would help, though I'd still wonder why we'd have separate Opts
> and Desc structs. IIUC, they generally carry (exactly) the same data.
>
> Maybe moving it into a `.handle` or `.desc` field in Shmem*Opts could
> make that part of the code a bit cleaner; as it'd further clarify that
> it's very much an optional field.
Yeah. OTOH, I'd like to separate the options from what's effectively a
return value. But maybe you're right and it's nevertheless better that way.
Some options on this:
a) What's in the patch now
static ShmemStructDesc pgssSharedStateDesc;
ShmemRequestStruct(&pgssSharedStateDesc,
.name = "pg_stat_statements",
.size = sizeof(pgssSharedState),
.ptr = (void **) &pgss);
b) Allow passing NULL for the desc
ShmemRequestStruct(NULL,
.name = "pg_stat_statements",
.size = sizeof(pgssSharedState),
.ptr = (void **) &pgss);
c) Return the Desc as a return value
static ShmemStructDesc *pgssSharedStateDesc;
pgssSharedStateDesc =
ShmemRequestStruct(.name = "pg_stat_statements",
.size = sizeof(pgssSharedState),
.ptr = (void **) &pgss);
In option c) you can just throw away the result if you don't need it. I
kind of like this as a notational thing. However it has some downsides:
This changes the return value to be a pointer. I'm thinking that
ShmemRequestStruct() palloc's the descriptor struct in TopMemoryContext.
This is a little ugly because the descriptor struct is leaked if the
caller throws it away. It's not a lot of memory, but still.
I'm also not sure how well this fits in with the SLRU code. On 'master',
you already have SlruCtlData which is like the "desc" struct. Would we
turn that into a pointer too, adding one indirection to all the SLRU
calls. It's probably fine from a performance point of view, but it feels
like it's going in the wrong direction.
d) Make it part of Opts, as you suggested
static ShmemStructDesc pgssSharedStateDesc;
ShmemRequestStruct(.name = "pg_stat_statements",
.size = sizeof(pgssSharedState),
.ptr = (void **) &pgss,
.desc = &pgssSharedStateDesc);
In the attached new version, though, I stepped back and decided to
remove the whole ShmemStructDesc after all. I still think having a
handle like that is a good idea, and the follow-up patches for resizing
need it. However, with option d) it can easily be added later. With
option d), it seems silly to have it be part of the patch now, when the
desc struct doesn't really do anything. SLRU's still have a similar
SlruDesc struct, however. For SLRUs it's essentially the same as the old
SlruCtlData struct before these patches.
The Desc structs were being used for one thing though: I used the 'size'
from the Desc struct in ProcGlobalShmemInit() to get the allocated size
of each shmem area. The size computation there is complicated enough
that I'd rather not repeat it, and avoiding the repeated size
calculation was the raison d'être for these patches. I replaced it with
global variables to hold the sizes from the ShmemRequest() step to
ShmemInit(). But that would be one case where having the desc would
already be useful. Then again, I'm not sure we want to expose the 'size'
in the descriptor like that anyway, because as soon as we make shmem
regions resizable, we might not be able to keep the size in the
descriptor up-to-date. The size of these structs won't change, but we
might not want to expose the information because it would be confusing
for other structs where it can change to show outdated information.
On a related note, when we add back the ".desc" concept later, is
".desc" a good name, or ".handle" as you also suggested? More widely, do
we call the concept and the struct a "handle" or "descriptor" or what?
Or if we follow the precedence with the existing SlruCtlData struct, it
could be ".ctl". I'm not a fan of the "Ctl" naming though, because we
already have a lot of structs with "Ctl" in the name and it's not always
clear whether a "Ctl" struct refers to the shared memory parts or the
handle to it. Now that the "desc" structs are not part of these patches
anymore, however, we can punt on that decision.
On 02/04/2026 09:58, Ashutosh Bapat wrote:
>>
>> I renamed it to AttachOrInitShmemIndexEntry, and the args to 'may_init'
>> and 'may_attach'. But more importantly I added comments to explain the
>> different usages. Hope that helps..
>
> The explanation in the prologue looks good. But the function is still
> confusing. Instead of if ... else fi ... chain, I feel organizing this
> as below would make it more readable. (this was part of one of my
> earlier edit patches).
> if (found)
> ...
> else
> {
> if (!may_init)
> error
> if (!index_entry)
> error
>
> ... rest of the code to initialize and attach
> }
>
> But other than that I don't have any other brilliant ideas.
I did another refactoring in this area: I split
AttachOrInitShmemIndexEntry() into separate AttachShmemIndexEntry() and
InitShmemIndexEntry functions again. There's a little bit of repetition
that way, but IMO it makes it much clearer overall.
Other changes in this patch version:
- I moved some of the stuff from shmem.h to a new shmem_internal.h
header. The idea is that what remains in shmem.h provides the public API
for allocating shared memory.
- I refactored the "after-startup request" code. It now detects the case
that some of the shmem areas, but not all, have already been initialized
and throws an error.
Still processing the rest of the feedback from the past days. This patch
version is also available at
https://github.com/hlinnaka/postgres/tree/shmem-init-refactor-11.
- Heikki
Commits
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Tidy up #ifdef USE_INJECTION_POINTS guards
- 9480c585df6c 19 (unreleased) landed
-
Convert all remaining subsystems to use the new shmem allocation API
- 9b5acad3f40f 19 (unreleased) landed
-
Convert buffer manager to use the new shmem allocation functions
- a4b6139dcceb 19 (unreleased) landed
-
Add alignment option to ShmemRequestStruct()
- dacfe81a0de5 19 (unreleased) landed
-
Convert AIO to use the new shmem allocation functions
- 58a1573385ed 19 (unreleased) landed
-
Convert SLRUs to use the new shmem allocation functions
- 2e0943a8597e 19 (unreleased) landed
-
Refactor shmem initialization code in predicate.c
- 4c9eca5afea0 19 (unreleased) landed
-
Use the new shmem allocation functions in a few core subsystems
- c6d55714ba4c 19 (unreleased) landed
-
Convert lwlock.c to use the new shmem allocation functions
- a006bc7b1699 19 (unreleased) landed
-
Introduce a registry of built-in shmem subsystems
- 1fc2e9fbc0a3 19 (unreleased) landed
-
Convert pg_stat_statements to use the new shmem allocation functions
- d4885af3d653 19 (unreleased) landed
-
Add a test module to test after-startup shmem allocations
- 6409994c7dd8 19 (unreleased) landed
-
Introduce a new mechanism for registering shared memory areas
- 283e823f9dcb 19 (unreleased) landed
-
Move some code from shmem.c and shmem.h
- 6ef9bee29310 19 (unreleased) landed
-
Improve test_lwlock_tranches
- 92a685e4070d 19 (unreleased) landed
-
Test pg_stat_statements across crash restart
- 148fe2b05df5 19 (unreleased) landed
-
Refactor PredicateLockShmemInit to not reuse var for different things
- 3fd057772827 19 (unreleased) landed
-
Refactor ShmemIndex initialization
- 6b8238cb6aa7 19 (unreleased) landed
-
Add a new shmem_request_hook hook.
- 4f2400cb3f10 15.0 cited