Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitaf720b4

Browse files
committed
Change custom wait events to use dynamic shared hash tables
Currently, the names of the custom wait event must be registered foreach backend, requiring all these to link to the shared memory area ofan extension, even if these are not loaded withshared_preload_libraries.This patch relaxes the constraints related to this infrastructure bystoring the wait events and their names in two dynamic hash tables inshared memory. This has the advantage to simplify the registration ofcustom wait events to a single routine call that returns an event IDready for consumption:uint32 WaitEventExtensionNew(const char *wait_event_name);The caller of this routine can then cache locally the ID returned, to beused for pgstat_report_wait_start(), WaitLatch() or a similar routine.The implementation uses two hash tables: one with a key based on theevent name to avoid duplicates and a second using the event ID as keyfor event lookups, like on pg_stat_activity. These tables can hold aminimum of 16 entries, and a maximum of 128 entries, which should be plentyenough.The code changes done in worker_spi show how things are simplified (mostof the code removed in this commit comes from there):- worker_spi_init() is gone.- No more shared memory hooks required (size requested andinitialization).- The custom wait event ID is cached in the process that needs to setit, with one single call to WaitEventExtensionNew() to retrieve it.Per suggestion from Andres Freund.Author: Masahiro Ikeda, with a few tweaks from me.Discussion:https://postgr.es/m/20230801032349.aaiuvhtrcvvcwzcx@awork3.anarazel.de
1 parent2a8b40e commitaf720b4

File tree

10 files changed

+165
-238
lines changed

10 files changed

+165
-238
lines changed

‎doc/src/sgml/monitoring.sgml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,9 +1121,8 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
11211121
<literal>LWLock</literal> types
11221122
to the list shown in <xref linkend="wait-event-extension-table"/> and
11231123
<xref linkend="wait-event-lwlock-table"/>. In some cases, the name
1124-
assigned by an extension will not be available in all server processes;
1125-
so an <literal>Extension</literal> or <literal>LWLock</literal> wait
1126-
event might be reported as just
1124+
of <literal>LWLock</literal> assigned by an extension will not be
1125+
available in all server processes; It might be reported as just
11271126
<quote><literal>extension</literal></quote> rather than the
11281127
extension-assigned name.
11291128
</para>

‎doc/src/sgml/xfunc.sgml

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3454,33 +3454,15 @@ if (!ptr)
34543454
</sect2>
34553455

34563456
<sect2 id="xfunc-addin-wait-events">
3457-
<title>Shared Memory andCustom Wait Events</title>
3457+
<title>Custom Wait Events</title>
34583458

34593459
<para>
34603460
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:
3461+
<literal>Extension</literal> by calling:
34683462
<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)
3463+
uint32 WaitEventExtensionNew(const char *wait_event_name)
34833464
</programlisting>
3465+
The wait event is associated to a user-facing custom string.
34843466
An example can be found in <filename>src/test/modules/worker_spi</filename>
34853467
in the PostgreSQL source tree.
34863468
</para>

‎src/backend/storage/lmgr/lwlocknames.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,4 @@ XactTruncationLock44
5353
# 45 was XactTruncationLock until removal of BackendRandomLock
5454
WrapLimitsVacuumLock46
5555
NotifyQueueTailLock47
56+
WaitEventExtensionLock48

‎src/backend/utils/activity/wait_event.c

Lines changed: 137 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,41 @@ uint32 *my_wait_event_info = &local_my_wait_event_info;
4545
#defineWAIT_EVENT_CLASS_MASK0xFF000000
4646
#defineWAIT_EVENT_ID_MASK0x0000FFFF
4747

48+
/*
49+
* Hash tables for storing custom wait event ids and their names in
50+
* shared memory.
51+
*
52+
* WaitEventExtensionHashById is used to find the name from a event id.
53+
* Any backend can search it to find custom wait events.
54+
*
55+
* WaitEventExtensionHashByName is used to find the event ID from a name.
56+
* It is used to ensure that no duplicated entries are registered.
57+
*
58+
* The size of the hash table is based on the assumption that
59+
* WAIT_EVENT_EXTENSION_BASH_INIT_SIZE is enough for most cases, and it seems
60+
* unlikely that the number of entries will reach
61+
* WAIT_EVENT_EXTENSION_BASH_MAX_SIZE.
62+
*/
63+
staticHTAB*WaitEventExtensionHashById;/* find names from IDs */
64+
staticHTAB*WaitEventExtensionHashByName;/* find IDs from names */
65+
66+
#defineWAIT_EVENT_EXTENSION_HASH_INIT_SIZE16
67+
#defineWAIT_EVENT_EXTENSION_HASH_MAX_SIZE128
68+
69+
/* hash table entries */
70+
typedefstructWaitEventExtensionEntryById
71+
{
72+
uint16event_id;/* hash key */
73+
charwait_event_name[NAMEDATALEN];/* custom wait event name */
74+
}WaitEventExtensionEntryById;
75+
76+
typedefstructWaitEventExtensionEntryByName
77+
{
78+
charwait_event_name[NAMEDATALEN];/* hash key */
79+
uint16event_id;/* wait event ID */
80+
}WaitEventExtensionEntryByName;
81+
82+
4883
/* dynamic allocation counter for custom wait events in extensions */
4984
typedefstructWaitEventExtensionCounterData
5085
{
@@ -59,58 +94,118 @@ static WaitEventExtensionCounterData *WaitEventExtensionCounter;
5994
#defineNUM_BUILTIN_WAIT_EVENT_EXTENSION\
6095
(WAIT_EVENT_EXTENSION_FIRST_USER_DEFINED - WAIT_EVENT_EXTENSION)
6196

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-
staticconstchar**WaitEventExtensionNames=NULL;
68-
staticintWaitEventExtensionNamesAllocated=0;
97+
/* wait event info for extensions */
98+
#defineWAIT_EVENT_EXTENSION_INFO(eventId)(PG_WAIT_EXTENSION | eventId)
6999

70100
staticconstchar*GetWaitEventExtensionIdentifier(uint16eventId);
71101

72102
/*
73-
* Return the space for dynamic allocation counter.
103+
* Return the space for dynamicshared hash tables and dynamicallocation counter.
74104
*/
75105
Size
76106
WaitEventExtensionShmemSize(void)
77107
{
78-
returnsizeof(WaitEventExtensionCounterData);
108+
Sizesz;
109+
110+
sz=MAXALIGN(sizeof(WaitEventExtensionCounterData));
111+
sz=add_size(sz,hash_estimate_size(WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
112+
sizeof(WaitEventExtensionEntryById)));
113+
sz=add_size(sz,hash_estimate_size(WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
114+
sizeof(WaitEventExtensionEntryByName)));
115+
returnsz;
79116
}
80117

81118
/*
82-
* Allocate shmem space for dynamic allocation counter.
119+
* Allocate shmem space for dynamicshared hash and dynamicallocation counter.
83120
*/
84121
void
85122
WaitEventExtensionShmemInit(void)
86123
{
87124
boolfound;
125+
HASHCTLinfo;
88126

89127
WaitEventExtensionCounter= (WaitEventExtensionCounterData*)
90128
ShmemInitStruct("WaitEventExtensionCounterData",
91-
WaitEventExtensionShmemSize(),&found);
129+
sizeof(WaitEventExtensionCounterData),&found);
92130

93131
if (!found)
94132
{
95133
/* initialize the allocation counter and its spinlock. */
96134
WaitEventExtensionCounter->nextId=NUM_BUILTIN_WAIT_EVENT_EXTENSION;
97135
SpinLockInit(&WaitEventExtensionCounter->mutex);
98136
}
137+
138+
/* initialize or attach the hash tables to store custom wait events */
139+
info.keysize=sizeof(uint16);
140+
info.entrysize=sizeof(WaitEventExtensionEntryById);
141+
WaitEventExtensionHashById=ShmemInitHash("WaitEventExtension hash by id",
142+
WAIT_EVENT_EXTENSION_HASH_INIT_SIZE,
143+
WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
144+
&info,
145+
HASH_ELEM |HASH_BLOBS);
146+
147+
/* key is a NULL-terminated string */
148+
info.keysize=sizeof(char[NAMEDATALEN]);
149+
info.entrysize=sizeof(WaitEventExtensionEntryByName);
150+
WaitEventExtensionHashByName=ShmemInitHash("WaitEventExtension hash by name",
151+
WAIT_EVENT_EXTENSION_HASH_INIT_SIZE,
152+
WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
153+
&info,
154+
HASH_ELEM |HASH_STRINGS);
99155
}
100156

101157
/*
102-
* Allocate a new event ID and return the wait event.
158+
* Allocate a new event ID and return the wait event info.
159+
*
160+
* If the wait event name is already defined, this does not allocate a new
161+
* entry; it returns the wait event information associated to the name.
103162
*/
104163
uint32
105-
WaitEventExtensionNew(void)
164+
WaitEventExtensionNew(constchar*wait_event_name)
106165
{
107166
uint16eventId;
167+
boolfound;
168+
WaitEventExtensionEntryByName*entry_by_name;
169+
WaitEventExtensionEntryById*entry_by_id;
170+
171+
/* Check the limit of the length of the event name */
172+
if (strlen(wait_event_name) >=NAMEDATALEN)
173+
elog(ERROR,
174+
"cannot use custom wait event string longer than %u characters",
175+
NAMEDATALEN-1);
176+
177+
/*
178+
* Check if the wait event info associated to the name is already defined,
179+
* and return it if so.
180+
*/
181+
LWLockAcquire(WaitEventExtensionLock,LW_SHARED);
182+
entry_by_name= (WaitEventExtensionEntryByName*)
183+
hash_search(WaitEventExtensionHashByName,wait_event_name,
184+
HASH_FIND,&found);
185+
LWLockRelease(WaitEventExtensionLock);
186+
if (found)
187+
returnWAIT_EVENT_EXTENSION_INFO(entry_by_name->event_id);
108188

109-
Assert(LWLockHeldByMeInMode(AddinShmemInitLock,LW_EXCLUSIVE));
189+
/*
190+
* Allocate and register a new wait event. Recheck if the event name
191+
* exists, as it could be possible that a concurrent process has inserted
192+
* one with the same name since the LWLock acquired again here was
193+
* previously released.
194+
*/
195+
LWLockAcquire(WaitEventExtensionLock,LW_EXCLUSIVE);
196+
entry_by_name= (WaitEventExtensionEntryByName*)
197+
hash_search(WaitEventExtensionHashByName,wait_event_name,
198+
HASH_FIND,&found);
199+
if (found)
200+
{
201+
LWLockRelease(WaitEventExtensionLock);
202+
returnWAIT_EVENT_EXTENSION_INFO(entry_by_name->event_id);
203+
}
110204

205+
/* Allocate a new event Id */
111206
SpinLockAcquire(&WaitEventExtensionCounter->mutex);
112207

113-
if (WaitEventExtensionCounter->nextId>PG_UINT16_MAX)
208+
if (WaitEventExtensionCounter->nextId >=WAIT_EVENT_EXTENSION_HASH_MAX_SIZE)
114209
{
115210
SpinLockRelease(&WaitEventExtensionCounter->mutex);
116211
ereport(ERROR,
@@ -122,64 +217,23 @@ WaitEventExtensionNew(void)
122217

123218
SpinLockRelease(&WaitEventExtensionCounter->mutex);
124219

125-
returnPG_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(uint32wait_event_info,
141-
constchar*wait_event_name)
142-
{
143-
uint32classId;
144-
uint16eventId;
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));
220+
/* Register the new wait event */
221+
entry_by_id= (WaitEventExtensionEntryById*)
222+
hash_search(WaitEventExtensionHashById,&eventId,
223+
HASH_ENTER,&found);
224+
Assert(!found);
225+
strlcpy(entry_by_id->wait_event_name,wait_event_name,
226+
sizeof(entry_by_id->wait_event_name));
160227

161-
/* Convert to array index. */
162-
eventId-=NUM_BUILTIN_WAIT_EVENT_EXTENSION;
228+
entry_by_name= (WaitEventExtensionEntryByName*)
229+
hash_search(WaitEventExtensionHashByName,wait_event_name,
230+
HASH_ENTER,&found);
231+
Assert(!found);
232+
entry_by_name->event_id=eventId;
163233

164-
/* If necessary, create or enlarge array. */
165-
if (eventId >=WaitEventExtensionNamesAllocated)
166-
{
167-
uint32newalloc;
168-
169-
newalloc=pg_nextpower2_32(Max(8,eventId+1));
170-
171-
if (WaitEventExtensionNames==NULL)
172-
WaitEventExtensionNames= (constchar**)
173-
MemoryContextAllocZero(TopMemoryContext,
174-
newalloc*sizeof(char*));
175-
else
176-
WaitEventExtensionNames=
177-
repalloc0_array(WaitEventExtensionNames,constchar*,
178-
WaitEventExtensionNamesAllocated,newalloc);
179-
WaitEventExtensionNamesAllocated=newalloc;
180-
}
234+
LWLockRelease(WaitEventExtensionLock);
181235

182-
WaitEventExtensionNames[eventId]=wait_event_name;
236+
returnWAIT_EVENT_EXTENSION_INFO(eventId);
183237
}
184238

185239
/*
@@ -188,23 +242,25 @@ WaitEventExtensionRegisterName(uint32 wait_event_info,
188242
staticconstchar*
189243
GetWaitEventExtensionIdentifier(uint16eventId)
190244
{
245+
boolfound;
246+
WaitEventExtensionEntryById*entry;
247+
191248
/* Built-in event? */
192249
if (eventId<NUM_BUILTIN_WAIT_EVENT_EXTENSION)
193250
return"Extension";
194251

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;
252+
/* It is a user-defined wait event, so lookup hash table. */
253+
LWLockAcquire(WaitEventExtensionLock,LW_SHARED);
254+
entry= (WaitEventExtensionEntryById*)
255+
hash_search(WaitEventExtensionHashById,&eventId,
256+
HASH_FIND,&found);
257+
LWLockRelease(WaitEventExtensionLock);
202258

203-
if (eventId >=WaitEventExtensionNamesAllocated||
204-
WaitEventExtensionNames[eventId]==NULL)
205-
return"extension";
259+
if (!entry)
260+
elog(ERROR,"could not find custom wait event name for ID %u",
261+
eventId);
206262

207-
returnWaitEventExtensionNames[eventId];
263+
returnentry->wait_event_name;
208264
}
209265

210266

‎src/backend/utils/activity/wait_event_names.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ WAIT_EVENT_DOCONLYLogicalRepWorker"Waiting to read or update the state of logi
317317
WAIT_EVENT_DOCONLYXactTruncation"Waiting to execute <function>pg_xact_status</function> or update the oldest transaction ID available to it."
318318
WAIT_EVENT_DOCONLYWrapLimitsVacuum"Waiting to update limits on transaction id and multixact consumption."
319319
WAIT_EVENT_DOCONLYNotifyQueueTail"Waiting to update limit on <command>NOTIFY</command> message storage."
320+
WAIT_EVENT_DOCONLYWaitEventExtension"Waiting to read or update custom wait events information for extensions."
320321

321322
WAIT_EVENT_DOCONLYXactBuffer"Waiting for I/O on a transaction status SLRU buffer."
322323
WAIT_EVENT_DOCONLYCommitTsBuffer"Waiting for I/O on a commit timestamp SLRU buffer."

‎src/include/utils/wait_event.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,14 @@ extern PGDLLIMPORT uint32 *my_wait_event_info;
4444
* Use this category when the server process is waiting for some condition
4545
* defined by an extension module.
4646
*
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.
47+
* Extensions can define their own wait events in this category. They should
48+
* call WaitEventExtensionNew() with a wait event string. If the wait event
49+
* associated to a string is already allocated, it returns the wait event
50+
* information to use. If not, it gets one wait event ID allocated from
51+
* a shared counter, associates the string to the ID in the shared dynamic
52+
* hash and returns the wait event information.
53+
*
54+
* The ID retrieved can be used with pgstat_report_wait_start() or equivalent.
5355
*/
5456
typedefenum
5557
{
@@ -60,9 +62,7 @@ typedef enum
6062
externvoidWaitEventExtensionShmemInit(void);
6163
externSizeWaitEventExtensionShmemSize(void);
6264

63-
externuint32WaitEventExtensionNew(void);
64-
externvoidWaitEventExtensionRegisterName(uint32wait_event_info,
65-
constchar*wait_event_name);
65+
externuint32WaitEventExtensionNew(constchar*wait_event_name);
6666

6767
/* ----------
6868
* pgstat_report_wait_start() -

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp