On 2023-07-12 09:36, Andres Freund wrote:
Hi,
On 2023-07-11 16:45:26 +0900, Michael Paquier wrote:
+/* ----------
+ * Wait Events - Extension
+ *
+ * Use this category when the server process is waiting for some
condition
+ * defined by an extension module.
+ *
+ * Extensions can define custom wait events. First, call
+ * WaitEventExtensionNewTranche() just once to obtain a new wait
event
+ * tranche. The ID is allocated from a shared counter. Next, each
+ * individual process using the tranche should call
+ * WaitEventExtensionRegisterTranche() to associate that wait event
with
+ * a name.
What does "tranche" mean here? For LWLocks it makes some sense, it's
used for
a set of lwlocks, not an individual one. But here that doesn't really
seem to
apply?
Thanks for useful comments.
OK, I will change to WaitEventExtensionNewId() and
WaitEventExtensionRegisterName().
+ * It may seem strange that each process using the tranche must
register it
+ * separately, but dynamic shared memory segments aren't guaranteed
to be
+ * mapped at the same address in all coordinating backends, so
storing the
+ * registration in the main shared memory segment wouldn't work for
that case.
+ */
I don't really see how this applies to wait events? There's no pointers
here...
Yes, I'll fix the comments.
+typedef enum
+{
+ WAIT_EVENT_EXTENSION = PG_WAIT_EXTENSION,
+ WAIT_EVENT_EXTENSION_FIRST_USER_DEFINED
+} WaitEventExtension;
+
+extern void WaitEventExtensionShmemInit(void);
+extern Size WaitEventExtensionShmemSize(void);
+
+extern uint32 WaitEventExtensionNewTranche(void);
+extern void WaitEventExtensionRegisterTranche(uint32 wait_event_info,
-slock_t *ShmemLock; /* spinlock for shared memory and LWLock
+slock_t *ShmemLock; /* spinlock for shared memory, LWLock
+ * allocation,
and named extension wait event
* allocation */
I'm doubtful that it's a good idea to reuse the spinlock further. Given
that
the patch adds WaitEventExtensionShmemInit(), why not just have a lock
in
there?
OK, I'll create a new spinlock for the purpose.
+/*
+ * Allocate a new event ID and return the wait event info.
+ */
+uint32
+WaitEventExtensionNewTranche(void)
+{
+ uint16 eventId;
+
+ SpinLockAcquire(ShmemLock);
+ eventId = (*WaitEventExtensionCounter)++;
+ SpinLockRelease(ShmemLock);
+
+ return PG_WAIT_EXTENSION | eventId;
+}
It seems quite possible to run out space in WaitEventExtensionCounter,
so this
should error out in that case.
OK, I'll do so.
+/*
+ * Register a dynamic tranche name in the lookup table of the current
process.
+ *
+ * This routine will save a pointer to the wait event tranche name
passed
+ * as an argument, so the name should be allocated in a
backend-lifetime context
+ * (shared memory, TopMemoryContext, static constant, or similar).
+ *
+ * The "wait_event_name" will be user-visible as a wait event name,
so try to
+ * use a name that fits the style for those.
+ */
+void
+WaitEventExtensionRegisterTranche(uint32 wait_event_info,
+ const char
*wait_event_name)
+{
+ uint16 eventId;
+
+ /* Check wait event class. */
+ Assert((wait_event_info & 0xFF000000) == PG_WAIT_EXTENSION);
+
+ eventId = wait_event_info & 0x0000FFFF;
+
+ /* This should only be called for user-defined tranches. */
+ if (eventId < NUM_BUILTIN_WAIT_EVENT_EXTENSION)
+ return;
Why not assert out in that case then?
OK, I'll add the assertion for eventID.
+/*
+ * Return the name of an Extension wait event ID.
+ */
+static const char *
+GetWaitEventExtensionIdentifier(uint16 eventId)
+{
+ /* Build-in tranche? */
*built
I will fix it.
+ if (eventId < NUM_BUILTIN_WAIT_EVENT_EXTENSION)
+ return "Extension";
+
+ /*
+ * It's an extension tranche, so look in
WaitEventExtensionTrancheNames[].
+ * However, it's possible that the tranche has never been registered
by
+ * calling WaitEventExtensionRegisterTranche() in the current
process, in
+ * which case give up and return "Extension".
+ */
+ eventId -= NUM_BUILTIN_WAIT_EVENT_EXTENSION;
+
+ if (eventId >= WaitEventExtensionTrancheNamesAllocated ||
+ WaitEventExtensionTrancheNames[eventId] == NULL)
+ return "Extension";
I'd return something different here, otherwise something that's
effectively a
bug is not distinguishable from the built
It is a good idea. It would be good to name it "unknown wait event", the
same as
pgstat_get_wait_activity(), etc.
+++ b/src/test/modules/test_custom_wait_events/t/001_basic.pl
@@ -0,0 +1,34 @@
+# Copyright (c) 2023, PostgreSQL Global Development Group
+
+use strict;
+use warnings;
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+my $node = PostgreSQL::Test::Cluster->new('main');
+
+$node->init;
+$node->append_conf(
+ 'postgresql.conf',
+ "shared_preload_libraries = 'test_custom_wait_events'"
+);
+$node->start;
I think this should also test registering a wait event later.
I see. I wasn't expecting that.
@@ -0,0 +1,188 @@
+/*--------------------------------------------------------------------------
+ *
+ * test_custom_wait_events.c
+ * Code for testing custom wait events
+ *
+ * This code initializes a custom wait event in shmem_request_hook()
and
+ * provide a function to launch a background worker waiting forever
+ * with the custom wait event.
Isn't this vast overkill? Why not just have a simple C function that
waits
with a custom wait event, until cancelled? That'd maybe 1/10th of the
LOC.
Are you saying that processing in the background worker is overkill?
If my understanding is correct, it is difficult to implement the test
without a background worker for this purpose. This is because the test
will execute commands serially, while a function waiting is executed in
a backend process, it is not possible to connect to another backend and
check the wait events on pg_stat_activity view.
Please let me know if my understanding is wrong.
Regards,
--
Masahiro Ikeda
NTT DATA CORPORATION