Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit c9af054

Browse files
committed
Support custom wait events for wait event type "Extension"
Two backend routines are added to allow extension to allocate and define custom wait events, all of these being allocated in the type "Extension": * WaitEventExtensionNew(), that allocates a wait event ID computed from a counter in shared memory. * WaitEventExtensionRegisterName(), to associate a custom string to the wait event ID allocated. Note that this includes an example of how to use this new facility in worker_spi with tests in TAP for various scenarios, and some documentation about how to use them. Any code in the tree that currently uses WAIT_EVENT_EXTENSION could switch to this new facility to define custom wait events. This is left as work for future patches. Author: Masahiro Ikeda Reviewed-by: Andres Freund, Michael Paquier, Tristan Partin, Bharath Rupireddy Discussion: https://postgr.es/m/b9f5411acda0cf15c8fbb767702ff43e@oss.nttdata.com
1 parent 39055cb commit c9af054

File tree

11 files changed

+393
-23
lines changed

11 files changed

+393
-23
lines changed

doc/src/sgml/monitoring.sgml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,11 +1117,14 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
11171117

11181118
<note>
11191119
<para>
1120-
Extensions can add <literal>LWLock</literal> types to the list shown in
1121-
<xref linkend="wait-event-lwlock-table"/>. In some cases, the name
1120+
Extensions can add <literal>Extension</literal> and
1121+
<literal>LWLock</literal> types
1122+
to the list shown in <xref linkend="wait-event-extension-table"/> and
1123+
<xref linkend="wait-event-lwlock-table"/>. In some cases, the name
11221124
assigned by an extension will not be available in all server processes;
1123-
so an <literal>LWLock</literal> wait event might be reported as
1124-
just <quote><literal>extension</literal></quote> rather than the
1125+
so an <literal>Extension</literal> or <literal>LWLock</literal> wait
1126+
event might be reported as just
1127+
<quote><literal>extension</literal></quote> rather than the
11251128
extension-assigned name.
11261129
</para>
11271130
</note>

doc/src/sgml/xfunc.sgml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3453,6 +3453,51 @@ if (!ptr)
34533453
</para>
34543454
</sect2>
34553455

3456+
<sect2 id="xfunc-addin-wait-events">
3457+
<title>Shared Memory and Custom Wait Events</title>
3458+
3459+
<para>
3460+
Add-ins can define custom wait events under the wait event type
3461+
<literal>Extension</literal>. The add-in's shared library must be
3462+
preloaded by specifying it in <literal>shared_preload_libraries</literal>,
3463+
and register a <literal>shmem_request_hook</literal> and a
3464+
<literal>shmem_startup_hook</literal> in its
3465+
<function>_PG_init</function> function.
3466+
<literal>shmem_request_hook</literal> can request a shared memory size
3467+
to be later used at startup by calling:
3468+
<programlisting>
3469+
void RequestAddinShmemSpace(int size)
3470+
</programlisting>
3471+
</para>
3472+
<para>
3473+
<literal>shmem_startup_hook</literal> can allocate in shared memory
3474+
custom wait events by calling while holding the LWLock
3475+
<function>AddinShmemInitLock</function> to avoid any race conditions:
3476+
<programlisting>
3477+
uint32 WaitEventExtensionNew(void)
3478+
</programlisting>
3479+
Next, each process needs to associate the wait event allocated previously
3480+
to a user-facing custom string, which is something done by calling:
3481+
<programlisting>
3482+
void WaitEventExtensionRegisterName(uint32 wait_event_info, const char *wait_event_name)
3483+
</programlisting>
3484+
An example can be found in <filename>src/test/modules/worker_spi</filename>
3485+
in the PostgreSQL source tree.
3486+
</para>
3487+
<para>
3488+
Custom wait events can be viewed in
3489+
<link linkend="monitoring-pg-stat-activity-view"><structname>pg_stat_activity</structname></link>:
3490+
<screen>
3491+
=# SELECT wait_event_type, wait_event FROM pg_stat_activity
3492+
WHERE backend_type ~ 'worker_spi';
3493+
wait_event_type | wait_event
3494+
-----------------+-----------------
3495+
Extension | worker_spi_main
3496+
(1 row)
3497+
</screen>
3498+
</para>
3499+
</sect2>
3500+
34563501
<sect2 id="extend-cpp">
34573502
<title>Using C++ for Extensibility</title>
34583503

src/backend/storage/ipc/ipci.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#include "storage/spin.h"
5050
#include "utils/guc.h"
5151
#include "utils/snapmgr.h"
52+
#include "utils/wait_event.h"
5253

5354
/* GUCs */
5455
int shared_memory_type = DEFAULT_SHARED_MEMORY_TYPE;
@@ -142,6 +143,7 @@ CalculateShmemSize(int *num_semaphores)
142143
size = add_size(size, SyncScanShmemSize());
143144
size = add_size(size, AsyncShmemSize());
144145
size = add_size(size, StatsShmemSize());
146+
size = add_size(size, WaitEventExtensionShmemSize());
145147
#ifdef EXEC_BACKEND
146148
size = add_size(size, ShmemBackendArraySize());
147149
#endif
@@ -301,6 +303,7 @@ CreateSharedMemoryAndSemaphores(void)
301303
SyncScanShmemInit();
302304
AsyncShmemInit();
303305
StatsShmemInit();
306+
WaitEventExtensionShmemInit();
304307

305308
#ifdef EXEC_BACKEND
306309

src/backend/utils/activity/generate-wait_event_types.pl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,11 @@
133133
foreach my $waitclass (sort { uc($a) cmp uc($b) } keys %hashwe)
134134
{
135135

136-
# Don't generate .c and .h files for LWLock and Lock, these are
137-
# handled independently.
136+
# Don't generate .c and .h files for Extension, LWLock and
137+
# Lock, these are handled independently.
138138
next
139-
if ( $waitclass eq 'WaitEventLWLock'
139+
if ( $waitclass eq 'WaitEventExtension'
140+
|| $waitclass eq 'WaitEventLWLock'
140141
|| $waitclass eq 'WaitEventLock');
141142

142143
my $last = $waitclass;

src/backend/utils/activity/wait_event.c

Lines changed: 170 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,18 @@
2222
*/
2323
#include "postgres.h"
2424

25+
#include "miscadmin.h"
26+
#include "port/pg_bitutils.h"
2527
#include "storage/lmgr.h" /* for GetLockNameFromTagType */
2628
#include "storage/lwlock.h" /* for GetLWLockIdentifier */
29+
#include "storage/spin.h"
30+
#include "utils/memutils.h"
2731
#include "utils/wait_event.h"
2832

2933

3034
static const char *pgstat_get_wait_activity(WaitEventActivity w);
3135
static const char *pgstat_get_wait_bufferpin(WaitEventBufferPin w);
3236
static const char *pgstat_get_wait_client(WaitEventClient w);
33-
static const char *pgstat_get_wait_extension(WaitEventExtension w);
3437
static const char *pgstat_get_wait_ipc(WaitEventIPC w);
3538
static const char *pgstat_get_wait_timeout(WaitEventTimeout w);
3639
static const char *pgstat_get_wait_io(WaitEventIO w);
@@ -42,6 +45,169 @@ uint32 *my_wait_event_info = &local_my_wait_event_info;
4245
#define WAIT_EVENT_CLASS_MASK 0xFF000000
4346
#define WAIT_EVENT_ID_MASK 0x0000FFFF
4447

48+
/* dynamic allocation counter for custom wait events in extensions */
49+
typedef struct WaitEventExtensionCounterData
50+
{
51+
int nextId; /* next ID to assign */
52+
slock_t mutex; /* protects the counter */
53+
} WaitEventExtensionCounterData;
54+
55+
/* pointer to the shared memory */
56+
static WaitEventExtensionCounterData *WaitEventExtensionCounter;
57+
58+
/* first event ID of custom wait events for extensions */
59+
#define NUM_BUILTIN_WAIT_EVENT_EXTENSION \
60+
(WAIT_EVENT_EXTENSION_FIRST_USER_DEFINED - WAIT_EVENT_EXTENSION)
61+
62+
/*
63+
* This is indexed by event ID minus NUM_BUILTIN_WAIT_EVENT_EXTENSION, and
64+
* stores the names of all dynamically-created event IDs known to the current
65+
* process. Any unused entries in the array will contain NULL.
66+
*/
67+
static const char **WaitEventExtensionNames = NULL;
68+
static int WaitEventExtensionNamesAllocated = 0;
69+
70+
static const char *GetWaitEventExtensionIdentifier(uint16 eventId);
71+
72+
/*
73+
* Return the space for dynamic allocation counter.
74+
*/
75+
Size
76+
WaitEventExtensionShmemSize(void)
77+
{
78+
return sizeof(WaitEventExtensionCounterData);
79+
}
80+
81+
/*
82+
* Allocate shmem space for dynamic allocation counter.
83+
*/
84+
void
85+
WaitEventExtensionShmemInit(void)
86+
{
87+
bool found;
88+
89+
WaitEventExtensionCounter = (WaitEventExtensionCounterData *)
90+
ShmemInitStruct("WaitEventExtensionCounterData",
91+
WaitEventExtensionShmemSize(), &found);
92+
93+
if (!found)
94+
{
95+
/* initialize the allocation counter and its spinlock. */
96+
WaitEventExtensionCounter->nextId = NUM_BUILTIN_WAIT_EVENT_EXTENSION;
97+
SpinLockInit(&WaitEventExtensionCounter->mutex);
98+
}
99+
}
100+
101+
/*
102+
* Allocate a new event ID and return the wait event.
103+
*/
104+
uint32
105+
WaitEventExtensionNew(void)
106+
{
107+
uint16 eventId;
108+
109+
Assert(LWLockHeldByMeInMode(AddinShmemInitLock, LW_EXCLUSIVE));
110+
111+
SpinLockAcquire(&WaitEventExtensionCounter->mutex);
112+
113+
if (WaitEventExtensionCounter->nextId > PG_UINT16_MAX)
114+
{
115+
SpinLockRelease(&WaitEventExtensionCounter->mutex);
116+
ereport(ERROR,
117+
errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
118+
errmsg("too many wait events for extensions"));
119+
}
120+
121+
eventId = WaitEventExtensionCounter->nextId++;
122+
123+
SpinLockRelease(&WaitEventExtensionCounter->mutex);
124+
125+
return PG_WAIT_EXTENSION | eventId;
126+
}
127+
128+
/*
129+
* Register a dynamic wait event name for extension in the lookup table
130+
* of the current process.
131+
*
132+
* This routine will save a pointer to the wait event name passed as an argument,
133+
* so the name should be allocated in a backend-lifetime context
134+
* (shared memory, TopMemoryContext, static constant, or similar).
135+
*
136+
* The "wait_event_name" will be user-visible as a wait event name, so try to
137+
* use a name that fits the style for those.
138+
*/
139+
void
140+
WaitEventExtensionRegisterName(uint32 wait_event_info,
141+
const char *wait_event_name)
142+
{
143+
uint32 classId;
144+
uint16 eventId;
145+
146+
classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
147+
eventId = wait_event_info & WAIT_EVENT_ID_MASK;
148+
149+
/* Check the wait event class. */
150+
if (classId != PG_WAIT_EXTENSION)
151+
ereport(ERROR,
152+
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
153+
errmsg("invalid wait event class %u", classId));
154+
155+
/* This should only be called for user-defined wait event. */
156+
if (eventId < NUM_BUILTIN_WAIT_EVENT_EXTENSION)
157+
ereport(ERROR,
158+
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
159+
errmsg("invalid wait event ID %u", eventId));
160+
161+
/* Convert to array index. */
162+
eventId -= NUM_BUILTIN_WAIT_EVENT_EXTENSION;
163+
164+
/* If necessary, create or enlarge array. */
165+
if (eventId >= WaitEventExtensionNamesAllocated)
166+
{
167+
uint32 newalloc;
168+
169+
newalloc = pg_nextpower2_32(Max(8, eventId + 1));
170+
171+
if (WaitEventExtensionNames == NULL)
172+
WaitEventExtensionNames = (const char **)
173+
MemoryContextAllocZero(TopMemoryContext,
174+
newalloc * sizeof(char *));
175+
else
176+
WaitEventExtensionNames =
177+
repalloc0_array(WaitEventExtensionNames, const char *,
178+
WaitEventExtensionNamesAllocated, newalloc);
179+
WaitEventExtensionNamesAllocated = newalloc;
180+
}
181+
182+
WaitEventExtensionNames[eventId] = wait_event_name;
183+
}
184+
185+
/*
186+
* Return the name of an wait event ID for extension.
187+
*/
188+
static const char *
189+
GetWaitEventExtensionIdentifier(uint16 eventId)
190+
{
191+
/* Built-in event? */
192+
if (eventId < NUM_BUILTIN_WAIT_EVENT_EXTENSION)
193+
return "Extension";
194+
195+
/*
196+
* It is a user-defined wait event, so look at WaitEventExtensionNames[].
197+
* However, it is possible that the name has never been registered by
198+
* calling WaitEventExtensionRegisterName() in the current process, in
199+
* which case give up and return "extension".
200+
*/
201+
eventId -= NUM_BUILTIN_WAIT_EVENT_EXTENSION;
202+
203+
if (eventId >= WaitEventExtensionNamesAllocated ||
204+
WaitEventExtensionNames[eventId] == NULL)
205+
return "extension";
206+
207+
return WaitEventExtensionNames[eventId];
208+
}
209+
210+
45211
/*
46212
* Configure wait event reporting to report wait events to *wait_event_info.
47213
* *wait_event_info needs to be valid until pgstat_reset_wait_event_storage()
@@ -151,6 +317,9 @@ pgstat_get_wait_event(uint32 wait_event_info)
151317
case PG_WAIT_LOCK:
152318
event_name = GetLockNameFromTagType(eventId);
153319
break;
320+
case PG_WAIT_EXTENSION:
321+
event_name = GetWaitEventExtensionIdentifier(eventId);
322+
break;
154323
case PG_WAIT_BUFFERPIN:
155324
{
156325
WaitEventBufferPin w = (WaitEventBufferPin) wait_event_info;
@@ -172,13 +341,6 @@ pgstat_get_wait_event(uint32 wait_event_info)
172341
event_name = pgstat_get_wait_client(w);
173342
break;
174343
}
175-
case PG_WAIT_EXTENSION:
176-
{
177-
WaitEventExtension w = (WaitEventExtension) wait_event_info;
178-
179-
event_name = pgstat_get_wait_extension(w);
180-
break;
181-
}
182344
case PG_WAIT_IPC:
183345
{
184346
WaitEventIPC w = (WaitEventIPC) wait_event_info;

src/backend/utils/activity/wait_event_names.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ WAIT_EVENT_BUFFER_PIN BufferPin "Waiting to acquire an exclusive pin on a buffer
261261

262262
Section: ClassName - WaitEventExtension
263263

264-
WAIT_EVENT_EXTENSION Extension "Waiting in an extension."
264+
WAIT_EVENT_DOCONLY Extension "Waiting in an extension."
265265

266266
#
267267
# Wait events - LWLock

src/include/utils/wait_event.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,32 @@ extern void pgstat_reset_wait_event_storage(void);
3838
extern PGDLLIMPORT uint32 *my_wait_event_info;
3939

4040

41+
/* ----------
42+
* Wait Events - Extension
43+
*
44+
* Use this category when the server process is waiting for some condition
45+
* defined by an extension module.
46+
*
47+
* Extensions can define their own wait events in this category. First,
48+
* they should call WaitEventExtensionNew() to get one or more wait event
49+
* IDs that are allocated from a shared counter. These can be used directly
50+
* with pgstat_report_wait_start() or equivalent. Next, each individual
51+
* process should call WaitEventExtensionRegisterName() to associate a wait
52+
* event string to the number allocated previously.
53+
*/
54+
typedef enum
55+
{
56+
WAIT_EVENT_EXTENSION = PG_WAIT_EXTENSION,
57+
WAIT_EVENT_EXTENSION_FIRST_USER_DEFINED
58+
} WaitEventExtension;
59+
60+
extern void WaitEventExtensionShmemInit(void);
61+
extern Size WaitEventExtensionShmemSize(void);
62+
63+
extern uint32 WaitEventExtensionNew(void);
64+
extern void WaitEventExtensionRegisterName(uint32 wait_event_info,
65+
const char *wait_event_name);
66+
4167
/* ----------
4268
* pgstat_report_wait_start() -
4369
*

0 commit comments

Comments
 (0)