]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Improve test_lwlock_tranches
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Sun, 5 Apr 2026 18:05:15 +0000 (21:05 +0300)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Sun, 5 Apr 2026 18:05:15 +0000 (21:05 +0300)
While working on refactoring how shmem is allocated, I made a mistake
where the main LWLock array did not reserve space for the LWLocks
allocated with RequestNamedLWLockTranche(), and the test still
passed. Matthias van de Meent spotted that before it got committed,
but in order to catch such mistakes in the future, add checks in
test_lwlock_tranches that the locks allocated with
RequestNamedLWLockTranche() can be acquired and released.

Another change is to stop requesting multiple tranches with the same
name with RequestNamedLWLockTranche(). As soon as I started to test
using the locks I realized that's bogus, and the next commit will
forbid it. Keep test coverage for duplicates requested with
LWLockNewTrancheId() for now, but make it more clear that that's what
the test does.

Reviewed-by: Sami Imseih <samimseih@gmail.com>
Discussion: https://www.postgresql.org/message-id/463a28db-0c0b-4af6-bac6-3891828bbbfe@iki.fi
Discussion: https://www.postgresql.org/message-id/CAEze2WjgCROMMXY0+j8FFdm3iFcr7By-+6Mwiz=PgGSEydiW3A@mail.gmail.com

src/test/modules/test_lwlock_tranches/expected/test_lwlock_tranches.out
src/test/modules/test_lwlock_tranches/sql/test_lwlock_tranches.sql
src/test/modules/test_lwlock_tranches/test_lwlock_tranches--1.0.sql
src/test/modules/test_lwlock_tranches/test_lwlock_tranches.c

index 688a20bca5d5c8db84d9399e030d48a025a3193a..fc25e42c4976c3ad4cccd6043f22e348a66e48b0 100644 (file)
@@ -1,25 +1,60 @@
 CREATE EXTENSION test_lwlock_tranches;
-SELECT test_lwlock_tranches();
- test_lwlock_tranches 
+-- Test lock tranches created with RequestNamedLWLockTranche()
+SELECT test_startup_lwlocks();
+ test_startup_lwlocks 
 ----------------------
  
 (1 row)
 
-SELECT test_lwlock_tranche_creation(NULL);
+-- Test creating new lock tranches with LWLockNewTrancheId()
+CREATE TEMP TABLE test_tranches(tranche_name text, tranche_id int);
+INSERT INTO test_tranches VALUES
+    ('test_tranche_a', test_lwlock_tranche_create('test_tranche_a')),
+    ('test_tranche_b', test_lwlock_tranche_create('test_tranche_b'));
+-- You can create multiple tranches with the same name. It's confusing, so
+-- not recommended, but test it.
+INSERT INTO test_tranches VALUES
+    ('test_tranche_duplicate', test_lwlock_tranche_create('test_tranche_duplicate')),
+    ('test_tranche_duplicate', test_lwlock_tranche_create('test_tranche_duplicate'));
+-- Check that all tranches were assigned different tranche IDs, including
+-- the ones with duplicate names
+select count(distinct tranche_id) from  test_tranches;
+ count 
+-------
+     4
+(1 row)
+
+-- Test GetLWLockIdentifier() on tranches created with LWLockNewTrancheId()
+select tranche_name, test_lwlock_get_lwlock_identifier(tranche_id) as lookup_result
+from test_tranches;
+      tranche_name      |     lookup_result      
+------------------------+------------------------
+ test_tranche_a         | test_tranche_a
+ test_tranche_b         | test_tranche_b
+ test_tranche_duplicate | test_tranche_duplicate
+ test_tranche_duplicate | test_tranche_duplicate
+(4 rows)
+
+-- negative tests
+SELECT test_lwlock_tranche_create(NULL);
 ERROR:  tranche name cannot be NULL
-SELECT test_lwlock_tranche_creation(repeat('a', 64));
+SELECT test_lwlock_tranche_create(repeat('a', 64));
 ERROR:  tranche name too long
 DETAIL:  LWLock tranche names must be no longer than 63 bytes.
-SELECT test_lwlock_tranche_creation('test');
-ERROR:  maximum number of tranches already registered
-DETAIL:  No more than 256 tranches may be registered.
-SELECT test_lwlock_tranche_lookup('test_lwlock_tranches_startup');
- test_lwlock_tranche_lookup 
-----------------------------
-(1 row)
-
 SELECT test_lwlock_tranche_lookup('bogus');
 ERROR:  requested tranche is not registered
+SELECT test_lwlock_tranche_lookup('test_tranche_a');
+ERROR:  requested tranche was not registered with RequestNamedLWLockTranche()
 SELECT test_lwlock_initialize(65535);
 ERROR:  tranche 65535 is not registered
+-- Test what happens when you use up all the slots.
+--
+-- MAX_USER_DEFINED_TRANCHES is 256. Two locks were created with
+-- RequestNamedLWLockTranche() when the library was loaded, and we created
+-- four more.
+insert into test_tranches
+    select 'test_tranche_consume_all', test_lwlock_tranche_create('test_tranche_consume_all')
+    from generate_series(1, 256 - 2 - 4);
+select test_lwlock_tranche_create('out-of-tranches');
+ERROR:  maximum number of tranches already registered
+DETAIL:  No more than 256 tranches may be registered.
index 4d085b8f25b7b478bb470c0747a0aae43a44bc6d..6f5fbcc3f237d23f1f7586697f755884e350ffc7 100644 (file)
@@ -1,8 +1,42 @@
 CREATE EXTENSION test_lwlock_tranches;
-SELECT test_lwlock_tranches();
-SELECT test_lwlock_tranche_creation(NULL);
-SELECT test_lwlock_tranche_creation(repeat('a', 64));
-SELECT test_lwlock_tranche_creation('test');
-SELECT test_lwlock_tranche_lookup('test_lwlock_tranches_startup');
+
+-- Test lock tranches created with RequestNamedLWLockTranche()
+SELECT test_startup_lwlocks();
+
+-- Test creating new lock tranches with LWLockNewTrancheId()
+CREATE TEMP TABLE test_tranches(tranche_name text, tranche_id int);
+INSERT INTO test_tranches VALUES
+    ('test_tranche_a', test_lwlock_tranche_create('test_tranche_a')),
+    ('test_tranche_b', test_lwlock_tranche_create('test_tranche_b'));
+
+-- You can create multiple tranches with the same name. It's confusing, so
+-- not recommended, but test it.
+INSERT INTO test_tranches VALUES
+    ('test_tranche_duplicate', test_lwlock_tranche_create('test_tranche_duplicate')),
+    ('test_tranche_duplicate', test_lwlock_tranche_create('test_tranche_duplicate'));
+
+-- Check that all tranches were assigned different tranche IDs, including
+-- the ones with duplicate names
+select count(distinct tranche_id) from  test_tranches;
+
+-- Test GetLWLockIdentifier() on tranches created with LWLockNewTrancheId()
+select tranche_name, test_lwlock_get_lwlock_identifier(tranche_id) as lookup_result
+from test_tranches;
+
+-- negative tests
+SELECT test_lwlock_tranche_create(NULL);
+SELECT test_lwlock_tranche_create(repeat('a', 64));
 SELECT test_lwlock_tranche_lookup('bogus');
+SELECT test_lwlock_tranche_lookup('test_tranche_a');
 SELECT test_lwlock_initialize(65535);
+
+-- Test what happens when you use up all the slots.
+--
+-- MAX_USER_DEFINED_TRANCHES is 256. Two locks were created with
+-- RequestNamedLWLockTranche() when the library was loaded, and we created
+-- four more.
+insert into test_tranches
+    select 'test_tranche_consume_all', test_lwlock_tranche_create('test_tranche_consume_all')
+    from generate_series(1, 256 - 2 - 4);
+
+select test_lwlock_tranche_create('out-of-tranches');
index 348219815214d1f4a7219f63b64256d6d0ea613f..9111580db48227987a7e960dabd355c268bf89d6 100644 (file)
@@ -3,14 +3,17 @@
 -- complain if script is sourced in psql, rather than via CREATE EXTENSION
 \echo Use "CREATE EXTENSION test_lwlock_tranches" to load this file. \quit
 
-CREATE FUNCTION test_lwlock_tranches() RETURNS VOID
+CREATE FUNCTION test_startup_lwlocks() RETURNS VOID
        AS 'MODULE_PATHNAME' LANGUAGE C;
 
-CREATE FUNCTION test_lwlock_tranche_creation(tranche_name TEXT) RETURNS VOID
+CREATE FUNCTION test_lwlock_tranche_create(tranche_name TEXT) RETURNS INT
        AS 'MODULE_PATHNAME' LANGUAGE C;
 
 CREATE FUNCTION test_lwlock_tranche_lookup(tranche_name TEXT) RETURNS VOID
        AS 'MODULE_PATHNAME' LANGUAGE C;
 
+CREATE FUNCTION test_lwlock_get_lwlock_identifier(event_id INT) RETURNS TEXT
+       AS 'MODULE_PATHNAME' LANGUAGE C;
+
 CREATE FUNCTION test_lwlock_initialize(tranche_id INT) RETURNS VOID
        AS 'MODULE_PATHNAME' LANGUAGE C;
index e58bc49d420f7a4a62bc1b484b2fe1f094d42255..9585578f18e69f160787b9414ddf54e7f3607c10 100644 (file)
 
 PG_MODULE_MAGIC;
 
-#define STARTUP_TRANCHE_NAME "test_lwlock_tranches_startup"
-#define DYNAMIC_TRANCHE_NAME "test_lwlock_tranches_dynamic"
-
-#define NUM_STARTUP_TRANCHES (32)
-#define NUM_DYNAMIC_TRANCHES (256 - NUM_STARTUP_TRANCHES)
-
-#define GET_TRANCHE_NAME(a) GetLWLockIdentifier(PG_WAIT_LWLOCK, (a))
-
 static shmem_request_hook_type prev_shmem_request_hook;
 static void test_lwlock_tranches_shmem_request(void);
 
@@ -45,36 +37,46 @@ test_lwlock_tranches_shmem_request(void)
        if (prev_shmem_request_hook)
                prev_shmem_request_hook();
 
-       for (int i = 0; i < NUM_STARTUP_TRANCHES; i++)
-               RequestNamedLWLockTranche(STARTUP_TRANCHE_NAME, 1);
+       /*
+        * Request some tranches with RequestNamedLWLockTranche() for
+        * test_startup_lwlocks()
+        */
+       RequestNamedLWLockTranche("test_lwlock_tranches_startup", 1);
+       RequestNamedLWLockTranche("test_lwlock_tranches_startup10", 10);
 }
 
 /*
- * Checks that GetLWLockIdentifier() returns the expected value for tranches
- * registered via RequestNamedLWLockTranche() and LWLockNewTrancheId().
+ * Test locks requested with RequestNamedLWLockTranche
  */
-PG_FUNCTION_INFO_V1(test_lwlock_tranches);
+PG_FUNCTION_INFO_V1(test_startup_lwlocks);
 Datum
-test_lwlock_tranches(PG_FUNCTION_ARGS)
+test_startup_lwlocks(PG_FUNCTION_ARGS)
 {
-       int                     dynamic_tranches[NUM_DYNAMIC_TRANCHES];
-
-       for (int i = 0; i < NUM_DYNAMIC_TRANCHES; i++)
-               dynamic_tranches[i] = LWLockNewTrancheId(DYNAMIC_TRANCHE_NAME);
-
-       for (int i = 0; i < NUM_STARTUP_TRANCHES; i++)
-       {
-               if (strcmp(GET_TRANCHE_NAME(LWTRANCHE_FIRST_USER_DEFINED + i),
-                                  STARTUP_TRANCHE_NAME) != 0)
-                       elog(ERROR, "incorrect startup lock tranche name");
-       }
-
-       for (int i = 0; i < NUM_DYNAMIC_TRANCHES; i++)
-       {
-               if (strcmp(GET_TRANCHE_NAME(dynamic_tranches[i]),
-                                  DYNAMIC_TRANCHE_NAME) != 0)
-                       elog(ERROR, "incorrect dynamic lock tranche name");
-       }
+       LWLockPadded *lwlock_startup;
+       LWLockPadded *lwlock_startup10;
+
+       /* Check that the locks can be used */
+       lwlock_startup = GetNamedLWLockTranche("test_lwlock_tranches_startup");
+       lwlock_startup10 = GetNamedLWLockTranche("test_lwlock_tranches_startup10");
+
+       LWLockAcquire(&lwlock_startup->lock, LW_EXCLUSIVE);
+       for (int i = 0; i < 10; i++)
+               LWLockAcquire(&lwlock_startup10[i].lock, LW_EXCLUSIVE);
+
+       LWLockRelease(&lwlock_startup->lock);
+       for (int i = 0; i < 10; i++)
+               LWLockRelease(&lwlock_startup10[i].lock);
+
+       /*
+        * Check that GetLWLockIdentifier() returns the expected value for
+        * tranches
+        */
+       if (strcmp("test_lwlock_tranches_startup",
+                          GetLWLockIdentifier(PG_WAIT_LWLOCK, LWTRANCHE_FIRST_USER_DEFINED)) != 0)
+               elog(ERROR, "incorrect startup lock tranche name");
+       if (strcmp("test_lwlock_tranches_startup10",
+                          GetLWLockIdentifier(PG_WAIT_LWLOCK, LWTRANCHE_FIRST_USER_DEFINED + 1)) != 0)
+               elog(ERROR, "incorrect startup lock tranche name");
 
        PG_RETURN_VOID();
 }
@@ -82,15 +84,16 @@ test_lwlock_tranches(PG_FUNCTION_ARGS)
 /*
  * Wrapper for LWLockNewTrancheId().
  */
-PG_FUNCTION_INFO_V1(test_lwlock_tranche_creation);
+PG_FUNCTION_INFO_V1(test_lwlock_tranche_create);
 Datum
-test_lwlock_tranche_creation(PG_FUNCTION_ARGS)
+test_lwlock_tranche_create(PG_FUNCTION_ARGS)
 {
        char       *tranche_name = PG_ARGISNULL(0) ? NULL : TextDatumGetCString(PG_GETARG_DATUM(0));
+       int                     tranche_id;
 
-       (void) LWLockNewTrancheId(tranche_name);
+       tranche_id = LWLockNewTrancheId(tranche_name);
 
-       PG_RETURN_VOID();
+       PG_RETURN_INT32(tranche_id);
 }
 
 /*
@@ -107,6 +110,21 @@ test_lwlock_tranche_lookup(PG_FUNCTION_ARGS)
        PG_RETURN_VOID();
 }
 
+PG_FUNCTION_INFO_V1(test_lwlock_get_lwlock_identifier);
+Datum
+test_lwlock_get_lwlock_identifier(PG_FUNCTION_ARGS)
+{
+       int                     eventId = PG_GETARG_INT32(0);
+       const char *tranche_name;
+
+       tranche_name = GetLWLockIdentifier(PG_WAIT_LWLOCK, eventId);
+
+       if (tranche_name)
+               PG_RETURN_TEXT_P(cstring_to_text(tranche_name));
+       else
+               PG_RETURN_NULL();
+}
+
 /*
  * Wrapper for LWLockInitialize().
  */