Split use of SerialSLRULock, creating SerialControlLock
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Tue, 30 Jan 2024 17:11:17 +0000 (18:11 +0100)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Tue, 30 Jan 2024 17:11:17 +0000 (18:11 +0100)
predicate.c has been using SerialSLRULock (the control lock for its SLRU
structure) to coordinate access to SerialControlData, another of its
numerous shared memory structures; this is unnecessary and confuses
further SLRU scalability work.  Create a separate LWLock to cover
SerialControlData.

Extracted from a larger patch from the same author, and some additional
changes by Álvaro.

Author: Dilip Kumar <dilip.kumar@enterprisedb.com>
Discussion: https://postgr.es/m/CAFiTN-vzDvNz=ExGXz6gdyjtzGixKSqs0mKHMmaQ8sOSEFZ33A@mail.gmail.com

src/backend/storage/lmgr/lwlocknames.txt
src/backend/storage/lmgr/predicate.c
src/backend/utils/activity/wait_event_names.txt

index a0163b21879749ec5f897595eb6a7e79555cafc8..3d59d3646e5a34d7f69bee57a87739e758cde5bf 100644 (file)
@@ -57,3 +57,4 @@ WaitEventExtensionLock                                48
 WALSummarizerLock                                      49
 DSMRegistryLock                                                50
 InjectionPointLock                             51
+SerialControlLock                                      52
index ee5ea1175c707d82bac4bbdb1aa575c19cfa7e55..eed63a05ed837f4c819baf9765353eabc3241ac0 100644 (file)
  *     SerializableXactHashLock
  *             - Protects both PredXact and SerializableXidHash.
  *
+ *     SerialControlLock
+ *             - Protects SerialControlData members
+ *
+ *     SerialSLRULock
+ *             - Protects SerialSlruCtl
  *
  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
@@ -828,9 +833,11 @@ SerialInit(void)
                /*
                 * Set control information to reflect empty SLRU.
                 */
+               LWLockAcquire(SerialControlLock, LW_EXCLUSIVE);
                serialControl->headPage = -1;
                serialControl->headXid = InvalidTransactionId;
                serialControl->tailXid = InvalidTransactionId;
+               LWLockRelease(SerialControlLock);
        }
 }
 
@@ -852,7 +859,12 @@ SerialAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo)
 
        targetPage = SerialPage(xid);
 
-       LWLockAcquire(SerialSLRULock, LW_EXCLUSIVE);
+       /*
+        * In this routine, we must hold both SerialControlLock and SerialSLRULock
+        * simultaneously while making the SLRU data catch up with the new state
+        * that we determine.
+        */
+       LWLockAcquire(SerialControlLock, LW_EXCLUSIVE);
 
        /*
         * If no serializable transactions are active, there shouldn't be anything
@@ -886,6 +898,8 @@ SerialAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo)
        if (isNewPage)
                serialControl->headPage = targetPage;
 
+       LWLockAcquire(SerialSLRULock, LW_EXCLUSIVE);
+
        if (isNewPage)
        {
                /* Initialize intervening pages. */
@@ -903,6 +917,7 @@ SerialAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo)
        SerialSlruCtl->shared->page_dirty[slotno] = true;
 
        LWLockRelease(SerialSLRULock);
+       LWLockRelease(SerialControlLock);
 }
 
 /*
@@ -920,10 +935,10 @@ SerialGetMinConflictCommitSeqNo(TransactionId xid)
 
        Assert(TransactionIdIsValid(xid));
 
-       LWLockAcquire(SerialSLRULock, LW_SHARED);
+       LWLockAcquire(SerialControlLock, LW_SHARED);
        headXid = serialControl->headXid;
        tailXid = serialControl->tailXid;
-       LWLockRelease(SerialSLRULock);
+       LWLockRelease(SerialControlLock);
 
        if (!TransactionIdIsValid(headXid))
                return 0;
@@ -954,7 +969,7 @@ SerialGetMinConflictCommitSeqNo(TransactionId xid)
 static void
 SerialSetActiveSerXmin(TransactionId xid)
 {
-       LWLockAcquire(SerialSLRULock, LW_EXCLUSIVE);
+       LWLockAcquire(SerialControlLock, LW_EXCLUSIVE);
 
        /*
         * When no sxacts are active, nothing overlaps, set the xid values to
@@ -966,7 +981,7 @@ SerialSetActiveSerXmin(TransactionId xid)
        {
                serialControl->tailXid = InvalidTransactionId;
                serialControl->headXid = InvalidTransactionId;
-               LWLockRelease(SerialSLRULock);
+               LWLockRelease(SerialControlLock);
                return;
        }
 
@@ -984,7 +999,7 @@ SerialSetActiveSerXmin(TransactionId xid)
                {
                        serialControl->tailXid = xid;
                }
-               LWLockRelease(SerialSLRULock);
+               LWLockRelease(SerialControlLock);
                return;
        }
 
@@ -993,7 +1008,7 @@ SerialSetActiveSerXmin(TransactionId xid)
 
        serialControl->tailXid = xid;
 
-       LWLockRelease(SerialSLRULock);
+       LWLockRelease(SerialControlLock);
 }
 
 /*
@@ -1007,12 +1022,12 @@ CheckPointPredicate(void)
 {
        int                     truncateCutoffPage;
 
-       LWLockAcquire(SerialSLRULock, LW_EXCLUSIVE);
+       LWLockAcquire(SerialControlLock, LW_EXCLUSIVE);
 
        /* Exit quickly if the SLRU is currently not in use. */
        if (serialControl->headPage < 0)
        {
-               LWLockRelease(SerialSLRULock);
+               LWLockRelease(SerialControlLock);
                return;
        }
 
@@ -1072,9 +1087,13 @@ CheckPointPredicate(void)
                serialControl->headPage = -1;
        }
 
-       LWLockRelease(SerialSLRULock);
+       LWLockRelease(SerialControlLock);
 
-       /* Truncate away pages that are no longer required */
+       /*
+        * Truncate away pages that are no longer required.  Note that no
+        * additional locking is required, because this is only called as part of
+        * a checkpoint, and the validity limits have already been determined.
+        */
        SimpleLruTruncate(SerialSlruCtl, truncateCutoffPage);
 
        /*
index a5df835dd417f73ed7da584aac6e3ada006e3a30..cd22dca7021e3a3d757a7e7a1d687b89de20037d 100644 (file)
@@ -331,6 +331,7 @@ WaitEventExtension  "Waiting to read or update custom wait events information for
 WALSummarizer  "Waiting to read or update WAL summarization state."
 DSMRegistry    "Waiting to read or update the dynamic shared memory registry."
 InjectionPoint "Waiting to read or update information related to injection points."
+SerialControl  "Waiting to read or update shared <filename>pg_serial</filename> state."
 
 #
 # END OF PREDEFINED LWLOCKS (DO NOT CHANGE THIS LINE)