From d8c251401b31e61458828fa1447556f9f8f8e02a Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Fri, 15 Aug 2025 17:53:03 +0000 Subject: [PATCH v8 2/2] Add tests for LWLock tranche names DSA --- src/backend/storage/lmgr/lwlock.c | 15 ++ 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, 395 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 93fddeed141..691ca302125 100644 --- a/src/backend/storage/lmgr/lwlock.c +++ b/src/backend/storage/lmgr/lwlock.c @@ -650,6 +650,7 @@ SetSharedTrancheName(int tranche_index, const char *tranche_name) int len; int current_allocated; int new_alloc = 0; + bool log = false; LWLockAcquire(&LWLockTrancheNames.shmem->lock, LW_EXCLUSIVE); @@ -666,6 +667,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); + + log = true; } /* @@ -696,6 +699,8 @@ SetSharedTrancheName(int tranche_index, const char *tranche_name) LWLockTrancheNames.shmem->list_ptr = new_list; LWLockTrancheNames.shmem->allocated = new_alloc; + + log = true; } /* Use the current list */ else @@ -713,6 +718,9 @@ SetSharedTrancheName(int tranche_index, const char *tranche_name) name_ptrs[tranche_index] = str_ptr; LWLockRelease(&LWLockTrancheNames.shmem->lock); + + if (log) + elog(DEBUG3, "current_allocated: %d in tranche names shared memory", new_alloc); } /* @@ -921,6 +929,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.39.5 (Apple Git-154)