From c4e47d76deb41e70b934faabdfb42ddd41bbef1c Mon Sep 17 00:00:00 2001
From: Ubuntu <ubuntu@ip-172-31-38-230.ec2.internal>
Date: Fri, 15 Aug 2025 17:53:03 +0000
Subject: [PATCH v10 2/2] Add tests for LWLock tranche names DSA

---
 src/backend/storage/lmgr/lwlock.c             |  11 ++
 src/backend/utils/activity/wait_event.c       |   3 -
 src/include/utils/wait_classes.h              |   2 +
 src/test/modules/Makefile                     |   3 +-
 src/test/modules/meson.build                  |   1 +
 .../modules/test_lwlock_tranches/Makefile     |  23 +++
 .../expected/test_lwlock_tranches.out         |  20 ++
 .../modules/test_lwlock_tranches/meson.build  |  33 ++++
 .../t/001_test_lwlock_tranches.pl             | 173 ++++++++++++++++++
 .../test_lwlock_tranches--1.0.sql             |  16 ++
 .../test_lwlock_tranches.c                    | 102 +++++++++++
 .../test_lwlock_tranches.conf                 |   2 +
 .../test_lwlock_tranches.control              |   6 +
 13 files changed, 391 insertions(+), 4 deletions(-)
 create mode 100644 src/test/modules/test_lwlock_tranches/Makefile
 create mode 100644 src/test/modules/test_lwlock_tranches/expected/test_lwlock_tranches.out
 create mode 100644 src/test/modules/test_lwlock_tranches/meson.build
 create mode 100644 src/test/modules/test_lwlock_tranches/t/001_test_lwlock_tranches.pl
 create mode 100644 src/test/modules/test_lwlock_tranches/test_lwlock_tranches--1.0.sql
 create mode 100644 src/test/modules/test_lwlock_tranches/test_lwlock_tranches.c
 create mode 100644 src/test/modules/test_lwlock_tranches/test_lwlock_tranches.conf
 create mode 100644 src/test/modules/test_lwlock_tranches/test_lwlock_tranches.control

diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 941b1ce321b..773acd1008d 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -672,6 +672,8 @@ SetSharedTrancheName(int tranche_index, const char *tranche_name)
 
 		name_ptrs = dsa_get_address(LWLockTrancheNames.dsa, LWLockTrancheNames.shmem->list_ptr);
 		memset(name_ptrs, InvalidDsaPointer, new_alloc);
+
+		elog(DEBUG3, "current_allocated: %d in tranche names shared memory", new_alloc);
 	}
 
 	/*
@@ -702,6 +704,8 @@ SetSharedTrancheName(int tranche_index, const char *tranche_name)
 
 		LWLockTrancheNames.shmem->list_ptr = new_list;
 		LWLockTrancheNames.shmem->allocated = new_alloc;
+
+		elog(DEBUG3, "current_allocated: %d in tranche names shared memory", new_alloc);
 	}
 	/* Use the current list */
 	else
@@ -937,6 +941,13 @@ GetLWTrancheName(uint16 trancheId)
 	if (!tranche_name)
 		elog(ERROR, "tranche ID %d is not registered", tranche_id_saved);
 
+	elog(DEBUG3,
+		 "tranche_name %s with tranche_id %d found at index %d. needs_sync: %s",
+		 tranche_name,
+		 tranche_id_saved,
+		 trancheId,
+		 needs_sync ? "true" : "false");
+
 	return tranche_name;
 }
 
diff --git a/src/backend/utils/activity/wait_event.c b/src/backend/utils/activity/wait_event.c
index d9b8f34a355..eba7d338c1f 100644
--- a/src/backend/utils/activity/wait_event.c
+++ b/src/backend/utils/activity/wait_event.c
@@ -39,9 +39,6 @@ static const char *pgstat_get_wait_io(WaitEventIO w);
 static uint32 local_my_wait_event_info;
 uint32	   *my_wait_event_info = &local_my_wait_event_info;
 
-#define WAIT_EVENT_CLASS_MASK	0xFF000000
-#define WAIT_EVENT_ID_MASK		0x0000FFFF
-
 /*
  * Hash tables for storing custom wait event ids and their names in
  * shared memory.
diff --git a/src/include/utils/wait_classes.h b/src/include/utils/wait_classes.h
index 51ee68397d5..6ca0504cee1 100644
--- a/src/include/utils/wait_classes.h
+++ b/src/include/utils/wait_classes.h
@@ -10,6 +10,8 @@
 #ifndef WAIT_CLASSES_H
 #define WAIT_CLASSES_H
 
+#define WAIT_EVENT_CLASS_MASK	0xFF000000
+#define WAIT_EVENT_ID_MASK		0x0000FFFF
 
 /* ----------
  * Wait Classes
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 903a8ac151a..e72800cd2b7 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -44,7 +44,8 @@ SUBDIRS = \
 		  test_tidstore \
 		  unsafe_tests \
 		  worker_spi \
-		  xid_wraparound
+		  xid_wraparound \
+		  test_lwlock_tranches
 
 
 ifeq ($(enable_injection_points),yes)
diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build
index 93be0f57289..f04910d13d7 100644
--- a/src/test/modules/meson.build
+++ b/src/test/modules/meson.build
@@ -45,3 +45,4 @@ subdir('typcache')
 subdir('unsafe_tests')
 subdir('worker_spi')
 subdir('xid_wraparound')
+subdir('test_lwlock_tranches')
diff --git a/src/test/modules/test_lwlock_tranches/Makefile b/src/test/modules/test_lwlock_tranches/Makefile
new file mode 100644
index 00000000000..9867c99cbfd
--- /dev/null
+++ b/src/test/modules/test_lwlock_tranches/Makefile
@@ -0,0 +1,23 @@
+# src/test/modules/test_lwlock_tranches/Makefile
+
+MODULE_big = test_lwlock_tranches
+OBJS = \
+	$(WIN32RES) \
+	test_lwlock_tranches.o
+PGFILEDESC = "test_lwlock_tranches - test code LWLock tranche management"
+
+EXTENSION = test_lwlock_tranches
+DATA = test_lwlock_tranches--1.0.sql
+
+TAP_TESTS = 1
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_lwlock_tranches
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_lwlock_tranches/expected/test_lwlock_tranches.out b/src/test/modules/test_lwlock_tranches/expected/test_lwlock_tranches.out
new file mode 100644
index 00000000000..337a8438a59
--- /dev/null
+++ b/src/test/modules/test_lwlock_tranches/expected/test_lwlock_tranches.out
@@ -0,0 +1,20 @@
+CREATE EXTENSION test_lwlock_tranches;
+SELECT test_lwlock_tranches_get_first_user_defined() AS num_user_defined \gset
+SELECT :num_user_defined;
+ ?column? 
+----------
+       97
+(1 row)
+
+SELECT test_get_lwlock_identifier(:num_user_defined);
+ test_get_lwlock_identifier 
+----------------------------
+ 
+(1 row)
+
+SELECT test_get_lwlock_identifier(:num_user_defined + 1);
+ test_get_lwlock_identifier 
+----------------------------
+ 
+(1 row)
+
diff --git a/src/test/modules/test_lwlock_tranches/meson.build b/src/test/modules/test_lwlock_tranches/meson.build
new file mode 100644
index 00000000000..f8aa700b58f
--- /dev/null
+++ b/src/test/modules/test_lwlock_tranches/meson.build
@@ -0,0 +1,33 @@
+# Copyright (c) 2024-2025, PostgreSQL Global Development Group
+
+test_lwlock_tranches_sources = files(
+  'test_lwlock_tranches.c',
+)
+
+if host_system == 'windows'
+  test_lwlock_tranches_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+    '--NAME', 'test_lwlock_tranches',
+    '--FILEDESC', 'test_lwlock_tranches - test code LWLock tranche management',])
+endif
+
+test_lwlock_tranches = shared_module('test_lwlock_tranches',
+  test_lwlock_tranches_sources,
+  kwargs: pg_test_mod_args,
+)
+test_install_libs += test_lwlock_tranches
+
+test_install_data += files(
+  'test_lwlock_tranches.control',
+  'test_lwlock_tranches--1.0.sql',
+)
+
+tests += {
+  'name': 'test_lwlock_tranches',
+  'sd': meson.current_source_dir(),
+  'bd': meson.current_build_dir(),
+  'tap': {
+    'tests': [
+      't/001_test_lwlock_tranches.pl',
+    ],
+  },
+}
\ No newline at end of file
diff --git a/src/test/modules/test_lwlock_tranches/t/001_test_lwlock_tranches.pl b/src/test/modules/test_lwlock_tranches/t/001_test_lwlock_tranches.pl
new file mode 100644
index 00000000000..f59925ea0b4
--- /dev/null
+++ b/src/test/modules/test_lwlock_tranches/t/001_test_lwlock_tranches.pl
@@ -0,0 +1,173 @@
+use strict;
+use warnings FATAL => 'all';
+
+use List::Util qw(min);
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+my $ENABLE_LOG_WAIT = 1;
+
+my $isession;
+my $log_location;
+
+# Helper function: wait for one or more logs if $ENABLE_LOG_WAIT is true
+sub maybe_wait_for_log {
+    my ($node, $logs, $log_loc) = @_;
+    return $log_loc unless $ENABLE_LOG_WAIT;
+
+    # Normalize single regex to array
+    $logs = [$logs] unless ref($logs) eq 'ARRAY';
+
+    foreach my $regex (@$logs) {
+        $log_loc = $node->wait_for_log($regex, $log_loc);
+    }
+    return $log_loc;
+}
+
+my $node = PostgreSQL::Test::Cluster->new('primary');
+$node->init();
+
+# The largest shared memory allocation we require for testing
+my $final_allocation = 64;
+
+# $unallocated_shared_memory will be overridden by the value
+# of $test_lwlock_tranches_requested_named_tranches when it's > 1
+my $unallocated_shared_memory = 2;
+my $requested_named_tranches = 2;
+
+# VALUES MATCHING INTERNALS
+# The initial array size. Should match LWLOCK_TRANCHE_NAMES_INIT_SIZE
+my $initial_size = 16;
+
+$node->append_conf('postgresql.conf',
+    qq(shared_preload_libraries='test_lwlock_tranches'));
+$node->append_conf('postgresql.conf',
+    qq(test_lwlock_tranches.requested_named_tranches=$requested_named_tranches));
+$node->append_conf('postgresql.conf',
+    qq(log_min_messages=DEBUG3));
+$node->start();
+
+$node->safe_psql('postgres', q(CREATE EXTENSION test_lwlock_tranches));
+
+my $first_user_defined = $node->safe_psql(
+    'postgres',
+    "SELECT test_lwlock_tranches_get_first_user_defined()"
+);
+
+my $next_index = 0;
+my $lookup_tranche_id = 0;
+my $current_size = $initial_size;
+
+$log_location = -s $node->logfile;
+
+if ($requested_named_tranches > 0) {
+    $unallocated_shared_memory += $requested_named_tranches;
+}
+
+if ($unallocated_shared_memory > 0) {
+    while ($current_size < $final_allocation) {
+        $current_size *= 2;
+    }
+
+    # Lookup before allocating shared memory
+    if ($requested_named_tranches > 0) {
+        for ($next_index = 0; $next_index < $requested_named_tranches; $next_index++) {
+            $lookup_tranche_id = $next_index + $first_user_defined;
+            $node->safe_psql('postgres',
+                qq{select test_get_lwlock_identifier($lookup_tranche_id)});
+            $log_location = maybe_wait_for_log(
+                $node,
+                qr/ DEBUG:  tranche_name test_lock_$next_index with tranche_id $lookup_tranche_id found at index $next_index./,
+                $log_location
+            );
+        }
+        ok(1, "requested_named_tranches looked up from local cache, before allocating shared memory");
+    }
+
+    # Create new tranches
+    $node->safe_psql('postgres',
+        qq{select test_lwlock_new_tranche_id($current_size - $unallocated_shared_memory)});
+    $log_location = maybe_wait_for_log(
+        $node,
+        qr/ DEBUG:  current_allocated: $final_allocation in tranche names shared memory/, $log_location
+    );
+
+    ok(1, "resize shared memory with allocation up to $current_size tranche names");
+
+    # Lookup after allocating shared memory
+    if ($requested_named_tranches > 0) {
+        for ($next_index = 0; $next_index < $requested_named_tranches; $next_index++) {
+            $lookup_tranche_id = $next_index + $first_user_defined;
+            $node->safe_psql('postgres',
+                qq{select test_get_lwlock_identifier($lookup_tranche_id)});
+            $log_location = maybe_wait_for_log(
+                $node,
+                qr/ DEBUG:  tranche_name test_lock_$next_index with tranche_id $lookup_tranche_id found at index $next_index./,
+                $log_location
+            );
+        }
+        ok(1, "requested_named_tranches looked up from local cache, after allocating shared memory");
+    }
+
+    $next_index = $requested_named_tranches;
+    $lookup_tranche_id = $next_index + $first_user_defined;
+    my $second_user_defined = $first_user_defined + 1;
+    $node->safe_psql('postgres',
+        qq{select test_get_lwlock_identifier($lookup_tranche_id);
+           select test_get_lwlock_identifier($lookup_tranche_id);
+           select test_get_lwlock_identifier($lookup_tranche_id);
+           select test_get_lwlock_identifier($first_user_defined);
+           select test_get_lwlock_identifier($second_user_defined)}
+    );
+    maybe_wait_for_log($node, qr/ DEBUG:  tranche_name test_lock__$next_index with tranche_id $lookup_tranche_id found at index $next_index. needs_sync: true/, $log_location);
+    maybe_wait_for_log($node, qr/ DEBUG:  tranche_name test_lock__$next_index with tranche_id $lookup_tranche_id found at index $next_index. needs_sync: false/, $log_location);
+    maybe_wait_for_log($node, qr/ DEBUG:  tranche_name test_lock__$next_index with tranche_id $lookup_tranche_id found at index $next_index. needs_sync: false/, $log_location);
+
+    for ($next_index = 0; $next_index < $requested_named_tranches; $next_index++) {
+        $lookup_tranche_id = $first_user_defined + $next_index;
+        maybe_wait_for_log($node, qr/ DEBUG:  tranche_name test_lock_$next_index with tranche_id $lookup_tranche_id found at index $next_index./, $log_location);
+    }
+    ok(1, "lookup with synchronization is successful");
+
+    $log_location = -s $node->logfile;
+
+    # Lookup unregistered tranches
+    $next_index = $current_size - $unallocated_shared_memory;
+    $lookup_tranche_id = $next_index + $first_user_defined;
+    $node->psql('postgres', qq{select test_get_lwlock_identifier($lookup_tranche_id)});
+    $log_location = maybe_wait_for_log($node, qr/ ERROR:  tranche ID $lookup_tranche_id is not registered/, $log_location);
+    ok(1, "test lookup within shared memory allocations");
+
+    $next_index = $current_size;
+    $lookup_tranche_id = $next_index + $first_user_defined;
+    $node->psql('postgres', qq{select test_get_lwlock_identifier($lookup_tranche_id)});
+    $log_location = maybe_wait_for_log($node, qr/ ERROR:  tranche ID $lookup_tranche_id is not registered/, $log_location);
+    ok(1, "test lookup outside shared memory allocations");
+}
+
+# Lookup when no tranche names are registered
+$node->append_conf('postgresql.conf',
+    qq(test_lwlock_tranches.requested_named_tranches=0));
+$node->stop();
+$node->start();
+
+my $last_possible = 65535 - $first_user_defined;
+my ($id1, $id2, $id3, $id4, $id5) = (0, $first_user_defined - 1, $first_user_defined, min($last_possible, $first_user_defined + 5000), $last_possible);
+
+$node->safe_psql('postgres', qq{select test_get_lwlock_identifier($id1);});
+$node->safe_psql('postgres', qq{select test_get_lwlock_identifier($id2);});
+ok(1, "check for no error when looking up built-in names");
+
+$node->psql('postgres', qq{select test_get_lwlock_identifier($id3);});
+$log_location = maybe_wait_for_log($node, qr/ ERROR:  tranche ID $id3 is not registered/, $log_location);
+
+$node->psql('postgres', qq{select test_get_lwlock_identifier($id4);});
+$log_location = maybe_wait_for_log($node, qr/ ERROR:  tranche ID $id4 is not registered/, $log_location);
+
+$node->psql('postgres', qq{select test_get_lwlock_identifier($id5);});
+$log_location = maybe_wait_for_log($node, qr/ ERROR:  tranche ID $id5 is not registered/, $log_location);
+
+ok(1, "check for error looking up user-defined names");
+
+done_testing();
diff --git a/src/test/modules/test_lwlock_tranches/test_lwlock_tranches--1.0.sql b/src/test/modules/test_lwlock_tranches/test_lwlock_tranches--1.0.sql
new file mode 100644
index 00000000000..7fdf90e3e23
--- /dev/null
+++ b/src/test/modules/test_lwlock_tranches/test_lwlock_tranches--1.0.sql
@@ -0,0 +1,16 @@
+-- test_lwlock_tranches--1.0.sql
+
+CREATE FUNCTION test_lwlock_new_tranche_id(bigint)
+RETURNS void
+AS 'MODULE_PATHNAME', 'test_lwlock_new_tranche_id'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION test_get_lwlock_identifier(int)
+RETURNS void
+AS 'MODULE_PATHNAME', 'test_get_lwlock_identifier'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION test_lwlock_tranches_get_first_user_defined()
+RETURNS int
+AS 'MODULE_PATHNAME', 'test_lwlock_tranches_get_first_user_defined'
+LANGUAGE C STRICT;
diff --git a/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.c b/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.c
new file mode 100644
index 00000000000..654ca9a5f44
--- /dev/null
+++ b/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.c
@@ -0,0 +1,102 @@
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "miscadmin.h"
+#include "storage/dsm_registry.h"
+#include "storage/ipc.h"
+#include "storage/lwlock.h"
+#include "utils/guc.h"
+#include "utils/injection_point.h"
+#include "utils/wait_classes.h"
+
+PG_MODULE_MAGIC;
+
+/* hooks */
+static shmem_request_hook_type prev_shmem_request_hook = NULL;
+static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
+static void test_lwlock_tranches_shmem_request(void);
+static void test_lwlock_tranches_shmem_startup(void);
+
+/* GUC */
+static int	test_lwlock_tranches_requested_named_tranches = 0;
+
+/*
+ * Module load callback
+ */
+void
+_PG_init(void)
+{
+	prev_shmem_request_hook = shmem_request_hook;
+	shmem_request_hook = test_lwlock_tranches_shmem_request;
+	prev_shmem_startup_hook = shmem_startup_hook;
+	shmem_startup_hook = test_lwlock_tranches_shmem_startup;
+
+	DefineCustomIntVariable("test_lwlock_tranches.requested_named_tranches",
+							"Sets the number of locks created during shmem request",
+							NULL,
+							&test_lwlock_tranches_requested_named_tranches,
+							2,
+							0,
+							UINT16_MAX,
+							PGC_POSTMASTER,
+							0,
+							NULL,
+							NULL,
+							NULL);
+}
+
+static void
+test_lwlock_tranches_shmem_startup(void)
+{
+	if (prev_shmem_startup_hook)
+		prev_shmem_startup_hook();
+}
+
+static void
+test_lwlock_tranches_shmem_request(void)
+{
+	if (prev_shmem_request_hook)
+		prev_shmem_request_hook();
+
+	for (int i = 0; i < test_lwlock_tranches_requested_named_tranches; i++)
+	{
+		char		name[15];
+
+		snprintf(name, sizeof(name), "test_lock_%d", i);
+		RequestNamedLWLockTranche(name, i);
+	}
+}
+
+PG_FUNCTION_INFO_V1(test_lwlock_new_tranche_id);
+Datum
+test_lwlock_new_tranche_id(PG_FUNCTION_ARGS)
+{
+	int64		num = PG_GETARG_INT64(0);
+
+	for (int i = test_lwlock_tranches_requested_named_tranches; i < num; i++)
+	{
+		char		name[50];
+
+		snprintf(name, 50, "test_lock__%d", i);
+
+		LWLockNewTrancheId(name);
+	}
+
+	PG_RETURN_VOID();
+}
+
+PG_FUNCTION_INFO_V1(test_get_lwlock_identifier);
+Datum
+test_get_lwlock_identifier(PG_FUNCTION_ARGS)
+{
+	GetLWLockIdentifier(PG_WAIT_LWLOCK & WAIT_EVENT_CLASS_MASK, PG_GETARG_INT32(0));
+
+	PG_RETURN_VOID();
+}
+
+PG_FUNCTION_INFO_V1(test_lwlock_tranches_get_first_user_defined);
+Datum
+test_lwlock_tranches_get_first_user_defined(PG_FUNCTION_ARGS)
+{
+	return LWTRANCHE_FIRST_USER_DEFINED;
+}
diff --git a/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.conf b/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.conf
new file mode 100644
index 00000000000..cd8c352900d
--- /dev/null
+++ b/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.conf
@@ -0,0 +1,2 @@
+shared_preload_libraries = 'test_lwlock_tranches'
+test_lwlock_tranches.requested_named_tranches = 2
diff --git a/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.control b/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.control
new file mode 100644
index 00000000000..bf0ecf64376
--- /dev/null
+++ b/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.control
@@ -0,0 +1,6 @@
+# test_lwlock_tranches.control
+
+comment = 'Test LWLock tranch names tracking'
+default_version = '1.0'
+relocatable = false
+module_pathname = '$libdir/test_lwlock_tranches'
\ No newline at end of file
-- 
2.43.0

