Improve accounting for PredXactList, RWConflictPool and PGPROC
authorTomas Vondra <[email protected]>
Wed, 2 Apr 2025 15:08:34 +0000 (17:08 +0200)
committerTomas Vondra <[email protected]>
Wed, 2 Apr 2025 15:14:28 +0000 (17:14 +0200)
Various places allocated shared memory by first allocating a small chunk
using ShmemInitStruct(), followed by ShmemAlloc() calls to allocate more
memory. Unfortunately, ShmemAlloc() does not update ShmemIndex, so this
affected pg_shmem_allocations - it only shown the initial chunk.

This commit modifies the following allocations, to allocate everything
as a single chunk, and then split it internally.

- PredXactList
- RWConflictPool
- PGPROC structures
- Fast-Path Lock Array

The fast-path lock array is allocated separately, not as a part of the
PGPROC structures allocation.

Author: Rahila Syed <[email protected]>
Reviewed-by: Andres Freund <[email protected]>
Reviewed-by: Nazir Bilal Yavuz <[email protected]>
Reviewed-by: Tomas Vondra <[email protected]>
Discussion: https://postgr.es/m/CAH2L28vHzRankszhqz7deXURxKncxfirnuW68zD7+hVAqaS5GQ@mail.gmail.com

src/backend/storage/lmgr/predicate.c
src/backend/storage/lmgr/proc.c

index 5b21a0539811488acd646755966fd8c9a9a94548..d82114ffca1650a4d3894137cbc06f9b184a510a 100644 (file)
@@ -1226,14 +1226,21 @@ PredicateLockShmemInit(void)
     */
    max_table_size *= 10;
 
+   requestSize = add_size(PredXactListDataSize,
+                          (mul_size((Size) max_table_size,
+                                    sizeof(SERIALIZABLEXACT))));
+
    PredXact = ShmemInitStruct("PredXactList",
-                              PredXactListDataSize,
+                              requestSize,
                               &found);
    Assert(found == IsUnderPostmaster);
    if (!found)
    {
        int         i;
 
+       /* clean everything, both the header and the element */
+       memset(PredXact, 0, requestSize);
+
        dlist_init(&PredXact->availableList);
        dlist_init(&PredXact->activeList);
        PredXact->SxactGlobalXmin = InvalidTransactionId;
@@ -1242,11 +1249,9 @@ PredicateLockShmemInit(void)
        PredXact->LastSxactCommitSeqNo = FirstNormalSerCommitSeqNo - 1;
        PredXact->CanPartialClearThrough = 0;
        PredXact->HavePartialClearedThrough = 0;
-       requestSize = mul_size((Size) max_table_size,
-                              sizeof(SERIALIZABLEXACT));
-       PredXact->element = ShmemAlloc(requestSize);
+       PredXact->element
+           = (SERIALIZABLEXACT *) ((char *) PredXact + PredXactListDataSize);
        /* Add all elements to available list, clean. */
-       memset(PredXact->element, 0, requestSize);
        for (i = 0; i < max_table_size; i++)
        {
            LWLockInitialize(&PredXact->element[i].perXactPredicateListLock,
@@ -1300,20 +1305,25 @@ PredicateLockShmemInit(void)
     */
    max_table_size *= 5;
 
+   requestSize = RWConflictPoolHeaderDataSize +
+       mul_size((Size) max_table_size,
+                RWConflictDataSize);
+
    RWConflictPool = ShmemInitStruct("RWConflictPool",
-                                    RWConflictPoolHeaderDataSize,
+                                    requestSize,
                                     &found);
    Assert(found == IsUnderPostmaster);
    if (!found)
    {
        int         i;
 
+       /* clean everything, including the elements */
+       memset(RWConflictPool, 0, requestSize);
+
        dlist_init(&RWConflictPool->availableList);
-       requestSize = mul_size((Size) max_table_size,
-                              RWConflictDataSize);
-       RWConflictPool->element = ShmemAlloc(requestSize);
+       RWConflictPool->element = (RWConflict) ((char *) RWConflictPool +
+                                               RWConflictPoolHeaderDataSize);
        /* Add all elements to available list, clean. */
-       memset(RWConflictPool->element, 0, requestSize);
        for (i = 0; i < max_table_size; i++)
        {
            dlist_push_tail(&RWConflictPool->availableList,
index 066319afe2b08c46a36a3a205718399dc44d756a..e9ef0fbfe32cb724744e2381fbe9c89561a35dbb 100644 (file)
@@ -91,26 +91,35 @@ static void CheckDeadLock(void);
 
 
 /*
- * Report shared-memory space needed by InitProcGlobal.
+ * Report shared-memory space needed by PGPROC.
  */
-Size
-ProcGlobalShmemSize(void)
+static Size
+PGProcShmemSize(void)
 {
    Size        size = 0;
    Size        TotalProcs =
        add_size(MaxBackends, add_size(NUM_AUXILIARY_PROCS, max_prepared_xacts));
-   Size        fpLockBitsSize,
-               fpRelIdSize;
 
-   /* ProcGlobal */
-   size = add_size(size, sizeof(PROC_HDR));
    size = add_size(size, mul_size(TotalProcs, sizeof(PGPROC)));
-   size = add_size(size, sizeof(slock_t));
-
    size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->xids)));
    size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->subxidStates)));
    size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->statusFlags)));
 
+   return size;
+}
+
+/*
+ * Report shared-memory space needed by Fast-Path locks.
+ */
+static Size
+FastPathLockShmemSize(void)
+{
+   Size        size = 0;
+   Size        TotalProcs =
+       add_size(MaxBackends, add_size(NUM_AUXILIARY_PROCS, max_prepared_xacts));
+   Size        fpLockBitsSize,
+               fpRelIdSize;
+
    /*
     * Memory needed for PGPROC fast-path lock arrays. Make sure the sizes are
     * nicely aligned in each backend.
@@ -123,6 +132,24 @@ ProcGlobalShmemSize(void)
    return size;
 }
 
+/*
+ * Report shared-memory space needed by InitProcGlobal.
+ */
+Size
+ProcGlobalShmemSize(void)
+{
+   Size        size = 0;
+
+   /* ProcGlobal */
+   size = add_size(size, sizeof(PROC_HDR));
+   size = add_size(size, sizeof(slock_t));
+
+   size = add_size(size, PGProcShmemSize());
+   size = add_size(size, FastPathLockShmemSize());
+
+   return size;
+}
+
 /*
  * Report number of semaphores needed by InitProcGlobal.
  */
@@ -175,6 +202,8 @@ InitProcGlobal(void)
               *fpEndPtr PG_USED_FOR_ASSERTS_ONLY;
    Size        fpLockBitsSize,
                fpRelIdSize;
+   Size        requestSize;
+   char       *ptr;
 
    /* Create the ProcGlobal shared structure */
    ProcGlobal = (PROC_HDR *)
@@ -204,8 +233,17 @@ InitProcGlobal(void)
     * with a single freelist.)  Each PGPROC structure is dedicated to exactly
     * one of these purposes, and they do not move between groups.
     */
-   procs = (PGPROC *) ShmemAlloc(TotalProcs * sizeof(PGPROC));
-   MemSet(procs, 0, TotalProcs * sizeof(PGPROC));
+   requestSize = PGProcShmemSize();
+
+   ptr = ShmemInitStruct("PGPROC structures",
+                         requestSize,
+                         &found);
+
+   MemSet(ptr, 0, requestSize);
+
+   procs = (PGPROC *) ptr;
+   ptr = (char *) ptr + TotalProcs * sizeof(PGPROC);
+
    ProcGlobal->allProcs = procs;
    /* XXX allProcCount isn't really all of them; it excludes prepared xacts */
    ProcGlobal->allProcCount = MaxBackends + NUM_AUXILIARY_PROCS;
@@ -217,13 +255,17 @@ InitProcGlobal(void)
     * XXX: It might make sense to increase padding for these arrays, given
     * how hotly they are accessed.
     */
-   ProcGlobal->xids =
-       (TransactionId *) ShmemAlloc(TotalProcs * sizeof(*ProcGlobal->xids));
-   MemSet(ProcGlobal->xids, 0, TotalProcs * sizeof(*ProcGlobal->xids));
-   ProcGlobal->subxidStates = (XidCacheStatus *) ShmemAlloc(TotalProcs * sizeof(*ProcGlobal->subxidStates));
-   MemSet(ProcGlobal->subxidStates, 0, TotalProcs * sizeof(*ProcGlobal->subxidStates));
-   ProcGlobal->statusFlags = (uint8 *) ShmemAlloc(TotalProcs * sizeof(*ProcGlobal->statusFlags));
-   MemSet(ProcGlobal->statusFlags, 0, TotalProcs * sizeof(*ProcGlobal->statusFlags));
+   ProcGlobal->xids = (TransactionId *) ptr;
+   ptr = (char *) ptr + (TotalProcs * sizeof(*ProcGlobal->xids));
+
+   ProcGlobal->subxidStates = (XidCacheStatus *) ptr;
+   ptr = (char *) ptr + (TotalProcs * sizeof(*ProcGlobal->subxidStates));
+
+   ProcGlobal->statusFlags = (uint8 *) ptr;
+   ptr = (char *) ptr + (TotalProcs * sizeof(*ProcGlobal->statusFlags));
+
+   /* make sure wer didn't overflow */
+   Assert((ptr > (char *) procs) && (ptr <= (char *) procs + requestSize));
 
    /*
     * Allocate arrays for fast-path locks. Those are variable-length, so
@@ -233,11 +275,16 @@ InitProcGlobal(void)
    fpLockBitsSize = MAXALIGN(FastPathLockGroupsPerBackend * sizeof(uint64));
    fpRelIdSize = MAXALIGN(FastPathLockSlotsPerBackend() * sizeof(Oid));
 
-   fpPtr = ShmemAlloc(TotalProcs * (fpLockBitsSize + fpRelIdSize));
-   MemSet(fpPtr, 0, TotalProcs * (fpLockBitsSize + fpRelIdSize));
+   requestSize = FastPathLockShmemSize();
+
+   fpPtr = ShmemInitStruct("Fast-Path Lock Array",
+                           requestSize,
+                           &found);
+
+   MemSet(fpPtr, 0, requestSize);
 
    /* For asserts checking we did not overflow. */
-   fpEndPtr = fpPtr + (TotalProcs * (fpLockBitsSize + fpRelIdSize));
+   fpEndPtr = fpPtr + requestSize;
 
    for (i = 0; i < TotalProcs; i++)
    {
@@ -330,7 +377,9 @@ InitProcGlobal(void)
    PreparedXactProcs = &procs[MaxBackends + NUM_AUXILIARY_PROCS];
 
    /* Create ProcStructLock spinlock, too */
-   ProcStructLock = (slock_t *) ShmemAlloc(sizeof(slock_t));
+   ProcStructLock = (slock_t *) ShmemInitStruct("ProcStructLock spinlock",
+                                                sizeof(slock_t),
+                                                &found);
    SpinLockInit(ProcStructLock);
 }