On Wed, Aug 21, 2024 at 12:46:31PM +0900, Michael Paquier wrote:
> Sorry, I should have split that for clarity (one patch for the GUC,
> one to change the test to use CACHED/LOAD).  It is not an error
> though: if we don't have a controlled way to disable the stats of the
> module, then the test would fail when calling the cached callback
> because we'd try to allocate some memory for the dshash entry in
> pgstats.
> 
> The second effect of initializing the shmem state of the module with
> shared_preload_libraries is condition variables are set up for the
> sake if the test, removing the dependency to the SQL load() call.
> Both are OK, but I'd prefer introducing one use case for these two
> macros in the tree, so as these can be used as a reference in the
> future when developing new tests.

In short, here is a better patch set, with 0001 and 0002 introducing
the pieces that the test would need to be able to use the LOAD() and
CACHED() macros in 0003:
- 0001: Add shmem callbacks to initialize shmem state of
injection_points with shared_preload_libraries.
- 0002: Add a GUC to control if the stats of the module are enabled.
By default, they are disabled as they are only needed in the TAP test
of injection_points for the stats.
- 0003: Update the SLRU test to use INJECTION_POINT_LOAD and
INJECTION_POINT_CACHED with injection_points loaded via
shared_preload_libraries, removing the call to
injection_points_load() in the perl test.

What do you think?
--
Michael
From 48a3b8b469ec52d184999d2617134ec680db378f Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Wed, 21 Aug 2024 15:12:08 +0900
Subject: [PATCH 1/3] injection_points: Add initialization of shmem state at
 loading

This commits adds callbacks to initialize the shared memory state of the
module when loaded with shared_preload_libraries.  This is necessary for
a upcoming change for a SLRU test, that relies on a critical section
where no allocation.  Initializing the shared memory state of the module
while loading provides a control on the timing where this data is set
up.
---
 .../injection_points/injection_points.c       | 65 ++++++++++++++++++-
 1 file changed, 62 insertions(+), 3 deletions(-)

diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c
index 4e775c7ec6..8d83d8c401 100644
--- a/src/test/modules/injection_points/injection_points.c
+++ b/src/test/modules/injection_points/injection_points.c
@@ -68,7 +68,12 @@ typedef struct InjectionPointCondition
  */
 static List *inj_list_local = NIL;
 
-/* Shared state information for injection points. */
+/*
+ * Shared state information for injection points.
+ *
+ * This state data can be initialized in two ways: dynamically with a DSM
+ * or when loading the module.
+ */
 typedef struct InjectionPointSharedState
 {
 	/* Protects access to other fields */
@@ -97,8 +102,13 @@ extern PGDLLEXPORT void injection_wait(const char *name,
 /* track if injection points attached in this process are linked to it */
 static bool injection_point_local = false;
 
+/* Shared memory init callbacks */
+static shmem_request_hook_type prev_shmem_request_hook = NULL;
+static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
+
 /*
- * Callback for shared memory area initialization.
+ * Routine for shared memory area initialization, used as a callback
+ * when initializing dynamically with a DSM or when loading the module.
  */
 static void
 injection_point_init_state(void *ptr)
@@ -111,8 +121,51 @@ injection_point_init_state(void *ptr)
 	ConditionVariableInit(&state->wait_point);
 }
 
+/* Shared memory initialization when loading module */
+static void
+injection_shmem_request(void)
+{
+	Size		size;
+
+	if (prev_shmem_request_hook)
+		prev_shmem_request_hook();
+
+	size = MAXALIGN(sizeof(InjectionPointSharedState));
+	RequestAddinShmemSpace(size);
+}
+
+static void
+injection_shmem_startup(void)
+{
+	bool		found;
+
+	if (prev_shmem_startup_hook)
+		prev_shmem_startup_hook();
+
+	/* reset in case this is a restart within the postmaster */
+	inj_state = NULL;
+
+	/* Create or attach to the shared memory state */
+	LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
+
+	inj_state = ShmemInitStruct("injection_points",
+								sizeof(InjectionPointSharedState),
+								&found);
+
+	if (!found)
+	{
+		/*
+		 * First time through, so initialize.  This is shared with the
+		 * dynamic initialization using a DSM.
+		 */
+		injection_point_init_state(inj_state);
+	}
+
+	LWLockRelease(AddinShmemInitLock);
+}
+
 /*
- * Initialize shared memory area for this module.
+ * Initialize shared memory area for this module through DSM.
  */
 static void
 injection_init_shmem(void)
@@ -463,6 +516,12 @@ _PG_init(void)
 	if (!process_shared_preload_libraries_in_progress)
 		return;
 
+	/* Shared memory initialization */
+	prev_shmem_request_hook = shmem_request_hook;
+	shmem_request_hook = injection_shmem_request;
+	prev_shmem_startup_hook = shmem_startup_hook;
+	shmem_startup_hook = injection_shmem_startup;
+
 	pgstat_register_inj();
 	pgstat_register_inj_fixed();
 }
-- 
2.45.2

From fd8ab7b6845a2c56aa2c8d9c60f404f6b3407338 Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Wed, 21 Aug 2024 15:16:06 +0900
Subject: [PATCH 2/3] injection_point: Add injection_points.stats

This GUC controls if statistics should be used or not in the module.
Custom statistics require the module to be loaded with
shared_preload_libraries, hence this GUC is made PGC_POSTMASTER.  By
default, stats are disabled.

This will be used by an upcoming change in a test where stats should not
be used, as the test has a dependency on a critical section.
---
 .../modules/injection_points/injection_points.c | 17 +++++++++++++++++
 .../modules/injection_points/injection_stats.c  |  8 ++++----
 .../modules/injection_points/injection_stats.h  |  3 +++
 .../injection_points/injection_stats_fixed.c    |  4 ++--
 .../modules/injection_points/t/001_stats.pl     |  6 ++++--
 5 files changed, 30 insertions(+), 8 deletions(-)

diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c
index 8d83d8c401..fb5eb3b586 100644
--- a/src/test/modules/injection_points/injection_points.c
+++ b/src/test/modules/injection_points/injection_points.c
@@ -28,6 +28,7 @@
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
 #include "utils/builtins.h"
+#include "utils/guc.h"
 #include "utils/injection_point.h"
 #include "utils/memutils.h"
 #include "utils/wait_event.h"
@@ -102,6 +103,9 @@ extern PGDLLEXPORT void injection_wait(const char *name,
 /* track if injection points attached in this process are linked to it */
 static bool injection_point_local = false;
 
+/* GUC variable */
+bool inj_stats_enabled = false;
+
 /* Shared memory init callbacks */
 static shmem_request_hook_type prev_shmem_request_hook = NULL;
 static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
@@ -516,6 +520,19 @@ _PG_init(void)
 	if (!process_shared_preload_libraries_in_progress)
 		return;
 
+	DefineCustomBoolVariable("injection_points.stats",
+							 "Enables statistics for injection points.",
+							 NULL,
+							 &inj_stats_enabled,
+							 false,
+							 PGC_POSTMASTER,
+							 0,
+							 NULL,
+							 NULL,
+							 NULL);
+
+	MarkGUCPrefixReserved("injection_points");
+
 	/* Shared memory initialization */
 	prev_shmem_request_hook = shmem_request_hook;
 	shmem_request_hook = injection_shmem_request;
diff --git a/src/test/modules/injection_points/injection_stats.c b/src/test/modules/injection_points/injection_stats.c
index 78042074ff..582686a0a8 100644
--- a/src/test/modules/injection_points/injection_stats.c
+++ b/src/test/modules/injection_points/injection_stats.c
@@ -91,7 +91,7 @@ pgstat_fetch_stat_injentry(const char *name)
 {
 	PgStat_StatInjEntry *entry = NULL;
 
-	if (!inj_stats_loaded)
+	if (!inj_stats_loaded || !inj_stats_enabled)
 		return NULL;
 
 	/* Compile the lookup key as a hash of the point name */
@@ -123,7 +123,7 @@ pgstat_create_inj(const char *name)
 	PgStatShared_InjectionPoint *shstatent;
 
 	/* leave if disabled */
-	if (!inj_stats_loaded)
+	if (!inj_stats_loaded || !inj_stats_enabled)
 		return;
 
 	entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_INJECTION, InvalidOid,
@@ -142,7 +142,7 @@ void
 pgstat_drop_inj(const char *name)
 {
 	/* leave if disabled */
-	if (!inj_stats_loaded)
+	if (!inj_stats_loaded || !inj_stats_enabled)
 		return;
 
 	if (!pgstat_drop_entry(PGSTAT_KIND_INJECTION, InvalidOid,
@@ -164,7 +164,7 @@ pgstat_report_inj(const char *name)
 	PgStat_StatInjEntry *statent;
 
 	/* leave if disabled */
-	if (!inj_stats_loaded)
+	if (!inj_stats_loaded || !inj_stats_enabled)
 		return;
 
 	entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_INJECTION, InvalidOid,
diff --git a/src/test/modules/injection_points/injection_stats.h b/src/test/modules/injection_points/injection_stats.h
index 126c110169..c48d533b4b 100644
--- a/src/test/modules/injection_points/injection_stats.h
+++ b/src/test/modules/injection_points/injection_stats.h
@@ -15,6 +15,9 @@
 #ifndef INJECTION_STATS
 #define INJECTION_STATS
 
+/* GUC variable */
+extern bool inj_stats_enabled;
+
 /* injection_stats.c */
 extern void pgstat_register_inj(void);
 extern void pgstat_create_inj(const char *name);
diff --git a/src/test/modules/injection_points/injection_stats_fixed.c b/src/test/modules/injection_points/injection_stats_fixed.c
index 82b07e5332..2fed178b7a 100644
--- a/src/test/modules/injection_points/injection_stats_fixed.c
+++ b/src/test/modules/injection_points/injection_stats_fixed.c
@@ -146,7 +146,7 @@ pgstat_report_inj_fixed(uint32 numattach,
 	PgStatShared_InjectionPointFixed *stats_shmem;
 
 	/* leave if disabled */
-	if (!inj_fixed_loaded)
+	if (!inj_fixed_loaded || !inj_stats_enabled)
 		return;
 
 	stats_shmem = pgstat_get_custom_shmem_data(PGSTAT_KIND_INJECTION_FIXED);
@@ -172,7 +172,7 @@ injection_points_stats_fixed(PG_FUNCTION_ARGS)
 	bool		nulls[5] = {0};
 	PgStat_StatInjFixedEntry *stats;
 
-	if (!inj_fixed_loaded)
+	if (!inj_fixed_loaded || !inj_stats_enabled)
 		PG_RETURN_NULL();
 
 	pgstat_snapshot_fixed(PGSTAT_KIND_INJECTION_FIXED);
diff --git a/src/test/modules/injection_points/t/001_stats.pl b/src/test/modules/injection_points/t/001_stats.pl
index 0d72cd86df..9df79b5168 100644
--- a/src/test/modules/injection_points/t/001_stats.pl
+++ b/src/test/modules/injection_points/t/001_stats.pl
@@ -20,8 +20,10 @@ if ($ENV{enable_injection_points} ne 'yes')
 # Node initialization
 my $node = PostgreSQL::Test::Cluster->new('master');
 $node->init;
-$node->append_conf('postgresql.conf',
-	"shared_preload_libraries = 'injection_points'");
+$node->append_conf('postgresql.conf', qq(
+shared_preload_libraries = 'injection_points'
+injection_points.stats = true
+));
 $node->start;
 $node->safe_psql('postgres', 'CREATE EXTENSION injection_points;');
 
-- 
2.45.2

From e5329d080b9d8436af8f65aac118745cf1f81ca2 Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Wed, 21 Aug 2024 15:09:06 +0900
Subject: [PATCH 3/3] Rework new SLRU test with injection points

Rather than the SQL injection_points_load, this commit makes the test
rely on the two macros to load and run an injection point from the
cache, acting as an example of how to use them.
---
 src/backend/access/transam/multixact.c        | 5 ++++-
 src/test/modules/test_slru/t/001_multixact.pl | 3 +--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 14c2b929e2..d2d0298ac3 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -855,6 +855,9 @@ MultiXactIdCreateFromMembers(int nmembers, MultiXactMember *members)
 		}
 	}
 
+	/* Load the injection point before entering the critical section */
+	INJECTION_POINT_LOAD("multixact-create-from-members");
+
 	/*
 	 * Assign the MXID and offsets range to use, and make sure there is space
 	 * in the OFFSETs and MEMBERs files.  NB: this routine does
@@ -869,7 +872,7 @@ MultiXactIdCreateFromMembers(int nmembers, MultiXactMember *members)
 	 */
 	multi = GetNewMultiXactId(nmembers, &offset);
 
-	INJECTION_POINT("multixact-create-from-members");
+	INJECTION_POINT_CACHED("multixact-create-from-members");
 
 	/* Make an XLOG entry describing the new MXID. */
 	xlrec.mid = multi;
diff --git a/src/test/modules/test_slru/t/001_multixact.pl b/src/test/modules/test_slru/t/001_multixact.pl
index f07406bf9d..882de7cd20 100644
--- a/src/test/modules/test_slru/t/001_multixact.pl
+++ b/src/test/modules/test_slru/t/001_multixact.pl
@@ -24,7 +24,7 @@ my ($node, $result);
 $node = PostgreSQL::Test::Cluster->new('mike');
 $node->init;
 $node->append_conf('postgresql.conf',
-	"shared_preload_libraries = 'test_slru'");
+	"shared_preload_libraries = 'test_slru,injection_points'");
 $node->start;
 $node->safe_psql('postgres', q(CREATE EXTENSION injection_points));
 $node->safe_psql('postgres', q(CREATE EXTENSION test_slru));
@@ -64,7 +64,6 @@ $node->safe_psql('postgres',
 $creator->query_until(
 	qr/start/, q{
 	\echo start
-	SELECT injection_points_load('multixact-create-from-members');
 	SELECT test_create_multixact();
 });
 
-- 
2.45.2

Attachment: signature.asc
Description: PGP signature

Reply via email to