v2-0003-Test-multixact-CV-sleep.patch
application/octet-stream
Filename: v2-0003-Test-multixact-CV-sleep.patch
Type: application/octet-stream
Part: 0
Patch
Format: format-patch
Series: patch v2-0003
Subject: Test multixact CV sleep
| File | + | − |
|---|---|---|
| src/backend/access/transam/multixact.c | 7 | 0 |
| src/backend/utils/misc/injection_point.c | 73 | 0 |
| src/include/utils/injection_point.h | 6 | 0 |
| src/test/modules/injection_points/injection_points--1.0.sql | 11 | 0 |
| src/test/modules/injection_points/injection_points.c | 29 | 0 |
| src/test/modules/injection_points/t/001_wait.pl | 43 | 0 |
From 51ba2eadfd2a4f56130d026febb939ecdaa0ea91 Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@night.local>
Date: Sun, 28 Jan 2024 23:10:55 +0500
Subject: [PATCH v2 3/3] Test multixact CV sleep
---
src/backend/access/transam/multixact.c | 7 ++
src/backend/utils/misc/injection_point.c | 73 +++++++++++++++++++
src/include/utils/injection_point.h | 6 ++
.../injection_points--1.0.sql | 11 +++
.../injection_points/injection_points.c | 29 ++++++++
.../modules/injection_points/t/001_wait.pl | 43 +++++++++++
6 files changed, 169 insertions(+)
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 03fcd25d4c..363a8bb1d7 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -89,6 +89,7 @@
#include "storage/proc.h"
#include "storage/procarray.h"
#include "utils/builtins.h"
+#include "utils/injection_point.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
@@ -822,8 +823,12 @@ MultiXactIdCreateFromMembers(int nmembers, MultiXactMember *members)
* in vacuum. During vacuum, in particular, it would be unacceptable to
* keep OldestMulti set, in case it runs for long.
*/
+ INJECTION_POINT_PREPARE("GetNewMultiXactId-done");
+
multi = GetNewMultiXactId(nmembers, &offset);
+ INJECTION_POINT_RUN_PREPARED();
+
/* Make an XLOG entry describing the new MXID. */
xlrec.mid = multi;
xlrec.moff = offset;
@@ -1408,6 +1413,8 @@ retry:
LWLockRelease(MultiXactOffsetSLRULock);
CHECK_FOR_INTERRUPTS();
+ INJECTION_POINT("GetMultiXactIdMembers-CV-sleep");
+
ConditionVariableSleep(&MultiXactState->nextoff_cv,
WAIT_EVENT_NEXT_MXMEMBERS);
ConditionVariableCancelSleep();
diff --git a/src/backend/utils/misc/injection_point.c b/src/backend/utils/misc/injection_point.c
index 398ef2cf30..c021889138 100644
--- a/src/backend/utils/misc/injection_point.c
+++ b/src/backend/utils/misc/injection_point.c
@@ -335,3 +335,76 @@ InjectionPointRun(const char *name)
elog(ERROR, "Injection points are not supported by this build");
#endif
}
+
+InjectionPointCallback prepared_injection_callback = NULL;
+const char* prepared_injection_callback_name;
+
+void
+InjectionPointPrepare(const char *name)
+{
+#ifdef USE_INJECTION_POINTS
+ InjectionPointEntry *entry_by_name;
+ bool found;
+ Assert(prepared_injection_callback == NULL);
+
+ LWLockAcquire(InjectionPointLock, LW_SHARED);
+ entry_by_name = (InjectionPointEntry *)
+ hash_search(InjectionPointHash, name,
+ HASH_FIND, &found);
+ LWLockRelease(InjectionPointLock);
+
+ /*
+ * If not found, do nothing and remove it from the local cache if it
+ * existed there.
+ */
+ if (!found)
+ {
+ injection_point_cache_remove(name);
+ return;
+ }
+
+ /*
+ * Check if the callback exists in the local cache, to avoid unnecessary
+ * external loads.
+ */
+ prepared_injection_callback = injection_point_cache_get(name);
+ if (prepared_injection_callback == NULL)
+ {
+ char path[MAXPGPATH];
+
+ /* not found in local cache, so load and register */
+ snprintf(path, MAXPGPATH, "%s/%s%s", pkglib_path,
+ entry_by_name->library, DLSUFFIX);
+
+ if (!pg_file_exists(path))
+ elog(ERROR, "could not find library \"%s\" for injection point \"%s\"",
+ path, name);
+
+ prepared_injection_callback = (InjectionPointCallback)
+ load_external_function(path, entry_by_name->function, false, NULL);
+
+ if (prepared_injection_callback == NULL)
+ elog(ERROR, "could not find function \"%s\" in library \"%s\" for injection point \"%s\"",
+ entry_by_name->function, path, name);
+
+ /* add it to the local cache when found */
+ injection_point_cache_add(name, prepared_injection_callback);
+ }
+
+ prepared_injection_callback_name = name;
+#else
+ elog(ERROR, "Injection points are not supported by this build");
+#endif
+}
+
+void
+InjectionPointRunPrepared()
+{
+#ifdef USE_INJECTION_POINTS
+ if (prepared_injection_callback != NULL)
+ prepared_injection_callback(prepared_injection_callback_name);
+ prepared_injection_callback = NULL;
+#else
+ elog(ERROR, "Injection points are not supported by this build");
+#endif
+}
diff --git a/src/include/utils/injection_point.h b/src/include/utils/injection_point.h
index e07f6b7024..73d3aeac8a 100644
--- a/src/include/utils/injection_point.h
+++ b/src/include/utils/injection_point.h
@@ -16,8 +16,12 @@
*/
#ifdef USE_INJECTION_POINTS
#define INJECTION_POINT(name) InjectionPointRun(name)
+#define INJECTION_POINT_PREPARE(name) InjectionPointPrepare(name)
+#define INJECTION_POINT_RUN_PREPARED() InjectionPointRunPrepared()
#else
#define INJECTION_POINT(name) ((void) name)
+#define INJECTION_POINT_PREPARE(name) ((void) name)
+#define INJECTION_POINT_RUN_PREPARED()
#endif
/*
@@ -34,5 +38,7 @@ extern void InjectionPointAttach(const char *name,
extern void InjectionPointRun(const char *name);
extern void InjectionPointDetach(const char *name);
extern bool InjectionPointIsAttach(const char *name);
+extern void InjectionPointPrepare(const char *name);
+extern void InjectionPointRunPrepared(void);
#endif /* INJECTION_POINT_H */
diff --git a/src/test/modules/injection_points/injection_points--1.0.sql b/src/test/modules/injection_points/injection_points--1.0.sql
index 5944c41716..d3ebda5964 100644
--- a/src/test/modules/injection_points/injection_points--1.0.sql
+++ b/src/test/modules/injection_points/injection_points--1.0.sql
@@ -33,3 +33,14 @@ CREATE FUNCTION injection_points_detach(IN point_name TEXT)
RETURNS void
AS 'MODULE_PATHNAME', 'injection_points_detach'
LANGUAGE C STRICT PARALLEL UNSAFE;
+
+
+CREATE FUNCTION create_test_multixact()
+RETURNS xid
+AS 'MODULE_PATHNAME', 'create_test_multixact'
+LANGUAGE C STRICT PARALLEL UNSAFE;
+
+CREATE FUNCTION read_test_multixact(xid)
+RETURNS void
+AS 'MODULE_PATHNAME', 'read_test_multixact'
+LANGUAGE C STRICT PARALLEL UNSAFE;
\ No newline at end of file
diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c
index fbb30b15ad..a0b9379886 100644
--- a/src/test/modules/injection_points/injection_points.c
+++ b/src/test/modules/injection_points/injection_points.c
@@ -109,3 +109,32 @@ injection_points_detach(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
+
+#include "access/multixact.h"
+#include "access/xact.h"
+
+PG_FUNCTION_INFO_V1(create_test_multixact);
+Datum
+create_test_multixact(PG_FUNCTION_ARGS)
+{
+ MultiXactId id;
+ MultiXactIdSetOldestMember();
+ id = MultiXactIdCreate(GetCurrentTransactionId(), MultiXactStatusUpdate,
+ GetCurrentTransactionId(), MultiXactStatusForShare);
+ PG_RETURN_TRANSACTIONID(id);
+}
+
+PG_FUNCTION_INFO_V1(read_test_multixact);
+Datum
+read_test_multixact(PG_FUNCTION_ARGS)
+{
+ MultiXactId id = PG_GETARG_TRANSACTIONID(0);
+ MultiXactMember *members;
+ INJECTION_POINT("read_test_multixact");
+ /* discard caches */
+ AtEOXact_MultiXact();
+
+ if (GetMultiXactIdMembers(id,&members,false, false) == -1)
+ elog(ERROR, "MultiXactId not found");
+ PG_RETURN_VOID();
+}
\ No newline at end of file
diff --git a/src/test/modules/injection_points/t/001_wait.pl b/src/test/modules/injection_points/t/001_wait.pl
index 98f7a8bcef..448dc97759 100644
--- a/src/test/modules/injection_points/t/001_wait.pl
+++ b/src/test/modules/injection_points/t/001_wait.pl
@@ -36,5 +36,48 @@ is($result, '0', 'wait injection point set');
$bg->quit;
+# Test for Multixact generation edge case
+$node->safe_psql('postgres', q(select injection_points_attach('read_test_multixact','wait')));
+$node->safe_psql('postgres', q(select injection_points_attach('GetMultiXactIdMembers-CV-sleep','notice')));
+
+# This session must observe sleep on CV when generating multixact.
+# To achive this it first will create a multixact, then pause before reading it.
+my $observer = $node->background_psql('postgres');
+
+$observer->query_until(qr/start/,
+q(
+ \echo start
+ select read_test_multixact(create_test_multixact());
+));
+
+# This session will create next Multixact, it's necessary to avoid edge case 1 (see multixact.c)
+my $creator = $node->background_psql('postgres');
+$node->safe_psql('postgres', q(select injection_points_attach('GetNewMultiXactId-done','wait')));
+
+# We expect this query to hand in critical section after generating new multixact,
+# but before filling it's offset into SLRU
+$creator->query_until(qr/start/, q(
+ \echo start
+ select create_test_multixact();
+));
+
+# Now we are sure we can reach edge case 2. Proceed session that is reading that multixact.
+$node->safe_psql('postgres', q(select injection_points_detach('read_test_multixact')));
+
+# Release critical section. We have to do this so everyon can proceed.
+# But this is inherent race condition, I hope the tast will not be unstable here.
+# The only way to stabilize it will be adding some sleep here.
+$node->safe_psql('postgres', q(select injection_points_detach('GetNewMultiXactId-done')));
+
+# Here goes the whole purpose of this test: see that sleep in fact occured.
+ok( pump_until(
+ $observer->{run}, $observer->{timeout},
+ \$observer->{stderr}, qr/notice triggered for injection point GetMultiXactIdMembers-CV-sleep/),
+ "sleep observed");
+
+$observer->quit;
+
+$creator->quit;
+
$node->stop;
done_testing();
--
2.37.1 (Apple Git-137.1)