Add wait event type "InjectionPoint", a custom type like "Extension".

Both injection points and customization of type "Extension" are new in
v17, so this just changes a detail of an unreleased feature.

Reported by Robert Haas.  Reviewed by Michael Paquier.

Discussion: https://postgr.es/m/CA+TgmobfMU5pdXP36D5iAwxV5WKE_vuDLtp_1QyH+H5jMMt21g@mail.gmail.com
This commit is contained in:
Noah Misch 2024-06-27 19:21:05 -07:00
parent 0844b39689
commit bb93640a68
13 changed files with 215 additions and 131 deletions

View File

@ -1063,6 +1063,14 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
See <xref linkend="wait-event-extension-table"/>. See <xref linkend="wait-event-extension-table"/>.
</entry> </entry>
</row> </row>
<row>
<entry><literal>InjectionPoint</literal></entry>
<entry>The server process is waiting for an injection point to reach an
outcome defined in a test. See
<xref linkend="xfunc-addin-injection-points"/> for more details. This
type has no predefined wait points.
</entry>
</row>
<row> <row>
<entry><literal>IO</literal></entry> <entry><literal>IO</literal></entry>
<entry>The server process is waiting for an I/O operation to complete. <entry>The server process is waiting for an I/O operation to complete.
@ -1139,8 +1147,8 @@ description | Waiting for a newly initialized WAL file to reach durable storage
<note> <note>
<para> <para>
Extensions can add <literal>Extension</literal> and Extensions can add <literal>Extension</literal>,
<literal>LWLock</literal> events <literal>InjectionPoint</literal>. and <literal>LWLock</literal> events
to the lists shown in <xref linkend="wait-event-extension-table"/> and to the lists shown in <xref linkend="wait-event-extension-table"/> and
<xref linkend="wait-event-lwlock-table"/>. In some cases, the name <xref linkend="wait-event-lwlock-table"/>. In some cases, the name
of an <literal>LWLock</literal> assigned by an extension will not be of an <literal>LWLock</literal> assigned by an extension will not be

View File

@ -3643,7 +3643,11 @@ extern void InjectionPointAttach(const char *name,
static void static void
custom_injection_callback(const char *name, const void *private_data) custom_injection_callback(const char *name, const void *private_data)
{ {
uint32 wait_event_info = WaitEventInjectionPointNew(name);
pgstat_report_wait_start(wait_event_info);
elog(NOTICE, "%s: executed custom callback", name); elog(NOTICE, "%s: executed custom callback", name);
pgstat_report_wait_end();
} }
</programlisting> </programlisting>
This callback prints a message to server error log with severity This callback prints a message to server error log with severity

View File

@ -149,7 +149,7 @@ CalculateShmemSize(int *num_semaphores)
size = add_size(size, SyncScanShmemSize()); size = add_size(size, SyncScanShmemSize());
size = add_size(size, AsyncShmemSize()); size = add_size(size, AsyncShmemSize());
size = add_size(size, StatsShmemSize()); size = add_size(size, StatsShmemSize());
size = add_size(size, WaitEventExtensionShmemSize()); size = add_size(size, WaitEventCustomShmemSize());
size = add_size(size, InjectionPointShmemSize()); size = add_size(size, InjectionPointShmemSize());
size = add_size(size, SlotSyncShmemSize()); size = add_size(size, SlotSyncShmemSize());
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
@ -355,7 +355,7 @@ CreateOrAttachShmemStructs(void)
SyncScanShmemInit(); SyncScanShmemInit();
AsyncShmemInit(); AsyncShmemInit();
StatsShmemInit(); StatsShmemInit();
WaitEventExtensionShmemInit(); WaitEventCustomShmemInit();
InjectionPointShmemInit(); InjectionPointShmemInit();
} }

View File

@ -181,9 +181,10 @@ if ($gen_code)
foreach my $waitclass (sort { uc($a) cmp uc($b) } keys %hashwe) foreach my $waitclass (sort { uc($a) cmp uc($b) } keys %hashwe)
{ {
# Don't generate the pgstat_wait_event.c and wait_event_types.h files # Don't generate the pgstat_wait_event.c and wait_event_types.h files
# for Extension, LWLock and Lock, these are handled independently. # for types handled independently.
next next
if ( $waitclass eq 'WaitEventExtension' if ( $waitclass eq 'WaitEventExtension'
|| $waitclass eq 'WaitEventInjectionPoint'
|| $waitclass eq 'WaitEventLWLock' || $waitclass eq 'WaitEventLWLock'
|| $waitclass eq 'WaitEventLock'); || $waitclass eq 'WaitEventLock');

View File

@ -47,68 +47,69 @@ uint32 *my_wait_event_info = &local_my_wait_event_info;
* Hash tables for storing custom wait event ids and their names in * Hash tables for storing custom wait event ids and their names in
* shared memory. * shared memory.
* *
* WaitEventExtensionHashById is used to find the name from an event id. * WaitEventCustomHashByInfo is used to find the name from wait event
* Any backend can search it to find custom wait events. * information. Any backend can search it to find custom wait events.
* *
* WaitEventExtensionHashByName is used to find the event ID from a name. * WaitEventCustomHashByName is used to find the wait event information from a
* It is used to ensure that no duplicated entries are registered. * name. It is used to ensure that no duplicated entries are registered.
*
* For simplicity, we use the same ID counter across types of custom events.
* We could end that anytime the need arises.
* *
* The size of the hash table is based on the assumption that * The size of the hash table is based on the assumption that
* WAIT_EVENT_EXTENSION_HASH_INIT_SIZE is enough for most cases, and it seems * WAIT_EVENT_CUSTOM_HASH_INIT_SIZE is enough for most cases, and it seems
* unlikely that the number of entries will reach * unlikely that the number of entries will reach
* WAIT_EVENT_EXTENSION_HASH_MAX_SIZE. * WAIT_EVENT_CUSTOM_HASH_MAX_SIZE.
*/ */
static HTAB *WaitEventExtensionHashById; /* find names from IDs */ static HTAB *WaitEventCustomHashByInfo; /* find names from infos */
static HTAB *WaitEventExtensionHashByName; /* find IDs from names */ static HTAB *WaitEventCustomHashByName; /* find infos from names */
#define WAIT_EVENT_EXTENSION_HASH_INIT_SIZE 16 #define WAIT_EVENT_CUSTOM_HASH_INIT_SIZE 16
#define WAIT_EVENT_EXTENSION_HASH_MAX_SIZE 128 #define WAIT_EVENT_CUSTOM_HASH_MAX_SIZE 128
/* hash table entries */ /* hash table entries */
typedef struct WaitEventExtensionEntryById typedef struct WaitEventCustomEntryByInfo
{ {
uint16 event_id; /* hash key */ uint32 wait_event_info; /* hash key */
char wait_event_name[NAMEDATALEN]; /* custom wait event name */ char wait_event_name[NAMEDATALEN]; /* custom wait event name */
} WaitEventExtensionEntryById; } WaitEventCustomEntryByInfo;
typedef struct WaitEventExtensionEntryByName typedef struct WaitEventCustomEntryByName
{ {
char wait_event_name[NAMEDATALEN]; /* hash key */ char wait_event_name[NAMEDATALEN]; /* hash key */
uint16 event_id; /* wait event ID */ uint32 wait_event_info;
} WaitEventExtensionEntryByName; } WaitEventCustomEntryByName;
/* dynamic allocation counter for custom wait events in extensions */ /* dynamic allocation counter for custom wait events */
typedef struct WaitEventExtensionCounterData typedef struct WaitEventCustomCounterData
{ {
int nextId; /* next ID to assign */ int nextId; /* next ID to assign */
slock_t mutex; /* protects the counter */ slock_t mutex; /* protects the counter */
} WaitEventExtensionCounterData; } WaitEventCustomCounterData;
/* pointer to the shared memory */ /* pointer to the shared memory */
static WaitEventExtensionCounterData *WaitEventExtensionCounter; static WaitEventCustomCounterData *WaitEventCustomCounter;
/* first event ID of custom wait events for extensions */ /* first event ID of custom wait events */
#define WAIT_EVENT_EXTENSION_INITIAL_ID 1 #define WAIT_EVENT_CUSTOM_INITIAL_ID 1
/* wait event info for extensions */ static uint32 WaitEventCustomNew(uint32 classId, const char *wait_event_name);
#define WAIT_EVENT_EXTENSION_INFO(eventId) (PG_WAIT_EXTENSION | eventId) static const char *GetWaitEventCustomIdentifier(uint32 wait_event_info);
static const char *GetWaitEventExtensionIdentifier(uint16 eventId);
/* /*
* Return the space for dynamic shared hash tables and dynamic allocation counter. * Return the space for dynamic shared hash tables and dynamic allocation counter.
*/ */
Size Size
WaitEventExtensionShmemSize(void) WaitEventCustomShmemSize(void)
{ {
Size sz; Size sz;
sz = MAXALIGN(sizeof(WaitEventExtensionCounterData)); sz = MAXALIGN(sizeof(WaitEventCustomCounterData));
sz = add_size(sz, hash_estimate_size(WAIT_EVENT_EXTENSION_HASH_MAX_SIZE, sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
sizeof(WaitEventExtensionEntryById))); sizeof(WaitEventCustomEntryByInfo)));
sz = add_size(sz, hash_estimate_size(WAIT_EVENT_EXTENSION_HASH_MAX_SIZE, sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
sizeof(WaitEventExtensionEntryByName))); sizeof(WaitEventCustomEntryByName)));
return sz; return sz;
} }
@ -116,39 +117,41 @@ WaitEventExtensionShmemSize(void)
* Allocate shmem space for dynamic shared hash and dynamic allocation counter. * Allocate shmem space for dynamic shared hash and dynamic allocation counter.
*/ */
void void
WaitEventExtensionShmemInit(void) WaitEventCustomShmemInit(void)
{ {
bool found; bool found;
HASHCTL info; HASHCTL info;
WaitEventExtensionCounter = (WaitEventExtensionCounterData *) WaitEventCustomCounter = (WaitEventCustomCounterData *)
ShmemInitStruct("WaitEventExtensionCounterData", ShmemInitStruct("WaitEventCustomCounterData",
sizeof(WaitEventExtensionCounterData), &found); sizeof(WaitEventCustomCounterData), &found);
if (!found) if (!found)
{ {
/* initialize the allocation counter and its spinlock. */ /* initialize the allocation counter and its spinlock. */
WaitEventExtensionCounter->nextId = WAIT_EVENT_EXTENSION_INITIAL_ID; WaitEventCustomCounter->nextId = WAIT_EVENT_CUSTOM_INITIAL_ID;
SpinLockInit(&WaitEventExtensionCounter->mutex); SpinLockInit(&WaitEventCustomCounter->mutex);
} }
/* initialize or attach the hash tables to store custom wait events */ /* initialize or attach the hash tables to store custom wait events */
info.keysize = sizeof(uint16); info.keysize = sizeof(uint32);
info.entrysize = sizeof(WaitEventExtensionEntryById); info.entrysize = sizeof(WaitEventCustomEntryByInfo);
WaitEventExtensionHashById = ShmemInitHash("WaitEventExtension hash by id", WaitEventCustomHashByInfo =
WAIT_EVENT_EXTENSION_HASH_INIT_SIZE, ShmemInitHash("WaitEventCustom hash by wait event information",
WAIT_EVENT_EXTENSION_HASH_MAX_SIZE, WAIT_EVENT_CUSTOM_HASH_INIT_SIZE,
&info, WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
HASH_ELEM | HASH_BLOBS); &info,
HASH_ELEM | HASH_BLOBS);
/* key is a NULL-terminated string */ /* key is a NULL-terminated string */
info.keysize = sizeof(char[NAMEDATALEN]); info.keysize = sizeof(char[NAMEDATALEN]);
info.entrysize = sizeof(WaitEventExtensionEntryByName); info.entrysize = sizeof(WaitEventCustomEntryByName);
WaitEventExtensionHashByName = ShmemInitHash("WaitEventExtension hash by name", WaitEventCustomHashByName =
WAIT_EVENT_EXTENSION_HASH_INIT_SIZE, ShmemInitHash("WaitEventCustom hash by name",
WAIT_EVENT_EXTENSION_HASH_MAX_SIZE, WAIT_EVENT_CUSTOM_HASH_INIT_SIZE,
&info, WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
HASH_ELEM | HASH_STRINGS); &info,
HASH_ELEM | HASH_STRINGS);
} }
/* /*
@ -159,11 +162,24 @@ WaitEventExtensionShmemInit(void)
*/ */
uint32 uint32
WaitEventExtensionNew(const char *wait_event_name) WaitEventExtensionNew(const char *wait_event_name)
{
return WaitEventCustomNew(PG_WAIT_EXTENSION, wait_event_name);
}
uint32
WaitEventInjectionPointNew(const char *wait_event_name)
{
return WaitEventCustomNew(PG_WAIT_INJECTIONPOINT, wait_event_name);
}
static uint32
WaitEventCustomNew(uint32 classId, const char *wait_event_name)
{ {
uint16 eventId; uint16 eventId;
bool found; bool found;
WaitEventExtensionEntryByName *entry_by_name; WaitEventCustomEntryByName *entry_by_name;
WaitEventExtensionEntryById *entry_by_id; WaitEventCustomEntryByInfo *entry_by_info;
uint32 wait_event_info;
/* Check the limit of the length of the event name */ /* Check the limit of the length of the event name */
if (strlen(wait_event_name) >= NAMEDATALEN) if (strlen(wait_event_name) >= NAMEDATALEN)
@ -175,13 +191,24 @@ WaitEventExtensionNew(const char *wait_event_name)
* Check if the wait event info associated to the name is already defined, * Check if the wait event info associated to the name is already defined,
* and return it if so. * and return it if so.
*/ */
LWLockAcquire(WaitEventExtensionLock, LW_SHARED); LWLockAcquire(WaitEventCustomLock, LW_SHARED);
entry_by_name = (WaitEventExtensionEntryByName *) entry_by_name = (WaitEventCustomEntryByName *)
hash_search(WaitEventExtensionHashByName, wait_event_name, hash_search(WaitEventCustomHashByName, wait_event_name,
HASH_FIND, &found); HASH_FIND, &found);
LWLockRelease(WaitEventExtensionLock); LWLockRelease(WaitEventCustomLock);
if (found) if (found)
return WAIT_EVENT_EXTENSION_INFO(entry_by_name->event_id); {
uint32 oldClassId;
oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
if (oldClassId != classId)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("wait event \"%s\" already exists in type \"%s\"",
wait_event_name,
pgstat_get_wait_event_type(entry_by_name->wait_event_info))));
return entry_by_name->wait_event_info;
}
/* /*
* Allocate and register a new wait event. Recheck if the event name * Allocate and register a new wait event. Recheck if the event name
@ -189,113 +216,123 @@ WaitEventExtensionNew(const char *wait_event_name)
* one with the same name since the LWLock acquired again here was * one with the same name since the LWLock acquired again here was
* previously released. * previously released.
*/ */
LWLockAcquire(WaitEventExtensionLock, LW_EXCLUSIVE); LWLockAcquire(WaitEventCustomLock, LW_EXCLUSIVE);
entry_by_name = (WaitEventExtensionEntryByName *) entry_by_name = (WaitEventCustomEntryByName *)
hash_search(WaitEventExtensionHashByName, wait_event_name, hash_search(WaitEventCustomHashByName, wait_event_name,
HASH_FIND, &found); HASH_FIND, &found);
if (found) if (found)
{ {
LWLockRelease(WaitEventExtensionLock); uint32 oldClassId;
return WAIT_EVENT_EXTENSION_INFO(entry_by_name->event_id);
LWLockRelease(WaitEventCustomLock);
oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
if (oldClassId != classId)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("wait event \"%s\" already exists in type \"%s\"",
wait_event_name,
pgstat_get_wait_event_type(entry_by_name->wait_event_info))));
return entry_by_name->wait_event_info;
} }
/* Allocate a new event Id */ /* Allocate a new event Id */
SpinLockAcquire(&WaitEventExtensionCounter->mutex); SpinLockAcquire(&WaitEventCustomCounter->mutex);
if (WaitEventExtensionCounter->nextId >= WAIT_EVENT_EXTENSION_HASH_MAX_SIZE) if (WaitEventCustomCounter->nextId >= WAIT_EVENT_CUSTOM_HASH_MAX_SIZE)
{ {
SpinLockRelease(&WaitEventExtensionCounter->mutex); SpinLockRelease(&WaitEventCustomCounter->mutex);
ereport(ERROR, ereport(ERROR,
errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many wait events for extensions")); errmsg("too many custom wait events"));
} }
eventId = WaitEventExtensionCounter->nextId++; eventId = WaitEventCustomCounter->nextId++;
SpinLockRelease(&WaitEventExtensionCounter->mutex); SpinLockRelease(&WaitEventCustomCounter->mutex);
/* Register the new wait event */ /* Register the new wait event */
entry_by_id = (WaitEventExtensionEntryById *) wait_event_info = classId | eventId;
hash_search(WaitEventExtensionHashById, &eventId, entry_by_info = (WaitEventCustomEntryByInfo *)
hash_search(WaitEventCustomHashByInfo, &wait_event_info,
HASH_ENTER, &found); HASH_ENTER, &found);
Assert(!found); Assert(!found);
strlcpy(entry_by_id->wait_event_name, wait_event_name, strlcpy(entry_by_info->wait_event_name, wait_event_name,
sizeof(entry_by_id->wait_event_name)); sizeof(entry_by_info->wait_event_name));
entry_by_name = (WaitEventExtensionEntryByName *) entry_by_name = (WaitEventCustomEntryByName *)
hash_search(WaitEventExtensionHashByName, wait_event_name, hash_search(WaitEventCustomHashByName, wait_event_name,
HASH_ENTER, &found); HASH_ENTER, &found);
Assert(!found); Assert(!found);
entry_by_name->event_id = eventId; entry_by_name->wait_event_info = wait_event_info;
LWLockRelease(WaitEventExtensionLock); LWLockRelease(WaitEventCustomLock);
return WAIT_EVENT_EXTENSION_INFO(eventId); return wait_event_info;
} }
/* /*
* Return the name of an wait event ID for extension. * Return the name of a custom wait event information.
*/ */
static const char * static const char *
GetWaitEventExtensionIdentifier(uint16 eventId) GetWaitEventCustomIdentifier(uint32 wait_event_info)
{ {
bool found; bool found;
WaitEventExtensionEntryById *entry; WaitEventCustomEntryByInfo *entry;
/* Built-in event? */ /* Built-in event? */
if (eventId < WAIT_EVENT_EXTENSION_INITIAL_ID) if (wait_event_info == PG_WAIT_EXTENSION)
return "Extension"; return "Extension";
/* It is a user-defined wait event, so lookup hash table. */ /* It is a user-defined wait event, so lookup hash table. */
LWLockAcquire(WaitEventExtensionLock, LW_SHARED); LWLockAcquire(WaitEventCustomLock, LW_SHARED);
entry = (WaitEventExtensionEntryById *) entry = (WaitEventCustomEntryByInfo *)
hash_search(WaitEventExtensionHashById, &eventId, hash_search(WaitEventCustomHashByInfo, &wait_event_info,
HASH_FIND, &found); HASH_FIND, &found);
LWLockRelease(WaitEventExtensionLock); LWLockRelease(WaitEventCustomLock);
if (!entry) if (!entry)
elog(ERROR, "could not find custom wait event name for ID %u", elog(ERROR,
eventId); "could not find custom name for wait event information %u",
wait_event_info);
return entry->wait_event_name; return entry->wait_event_name;
} }
/* /*
* Returns a list of currently defined custom wait event names for extensions. * Returns a list of currently defined custom wait event names. The result is
* The result is a palloc'd array, with the number of elements saved in * a palloc'd array, with the number of elements saved in *nwaitevents.
* *nwaitevents.
*/ */
char ** char **
GetWaitEventExtensionNames(int *nwaitevents) GetWaitEventCustomNames(uint32 classId, int *nwaitevents)
{ {
char **waiteventnames; char **waiteventnames;
WaitEventExtensionEntryByName *hentry; WaitEventCustomEntryByName *hentry;
HASH_SEQ_STATUS hash_seq; HASH_SEQ_STATUS hash_seq;
int index; int index;
int els; int els;
LWLockAcquire(WaitEventExtensionLock, LW_SHARED); LWLockAcquire(WaitEventCustomLock, LW_SHARED);
/* Now we can safely count the number of entries */ /* Now we can safely count the number of entries */
els = hash_get_num_entries(WaitEventExtensionHashByName); els = hash_get_num_entries(WaitEventCustomHashByName);
/* Allocate enough space for all entries */ /* Allocate enough space for all entries */
waiteventnames = palloc(els * sizeof(char *)); waiteventnames = palloc(els * sizeof(char *));
/* Now scan the hash table to copy the data */ /* Now scan the hash table to copy the data */
hash_seq_init(&hash_seq, WaitEventExtensionHashByName); hash_seq_init(&hash_seq, WaitEventCustomHashByName);
index = 0; index = 0;
while ((hentry = (WaitEventExtensionEntryByName *) hash_seq_search(&hash_seq)) != NULL) while ((hentry = (WaitEventCustomEntryByName *) hash_seq_search(&hash_seq)) != NULL)
{ {
if ((hentry->wait_event_info & WAIT_EVENT_CLASS_MASK) != classId)
continue;
waiteventnames[index] = pstrdup(hentry->wait_event_name); waiteventnames[index] = pstrdup(hentry->wait_event_name);
index++; index++;
} }
LWLockRelease(WaitEventExtensionLock); LWLockRelease(WaitEventCustomLock);
Assert(index == els);
*nwaitevents = index; *nwaitevents = index;
return waiteventnames; return waiteventnames;
@ -374,6 +411,9 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
case PG_WAIT_IO: case PG_WAIT_IO:
event_type = "IO"; event_type = "IO";
break; break;
case PG_WAIT_INJECTIONPOINT:
event_type = "InjectionPoint";
break;
default: default:
event_type = "???"; event_type = "???";
break; break;
@ -411,7 +451,8 @@ pgstat_get_wait_event(uint32 wait_event_info)
event_name = GetLockNameFromTagType(eventId); event_name = GetLockNameFromTagType(eventId);
break; break;
case PG_WAIT_EXTENSION: case PG_WAIT_EXTENSION:
event_name = GetWaitEventExtensionIdentifier(eventId); case PG_WAIT_INJECTIONPOINT:
event_name = GetWaitEventCustomIdentifier(wait_event_info);
break; break;
case PG_WAIT_BUFFERPIN: case PG_WAIT_BUFFERPIN:
{ {

View File

@ -48,7 +48,7 @@ pg_get_wait_events(PG_FUNCTION_ARGS)
#define PG_GET_WAIT_EVENTS_COLS 3 #define PG_GET_WAIT_EVENTS_COLS 3
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
char **waiteventnames; char **waiteventnames;
int nbextwaitevents; int nbwaitevents;
/* Build tuplestore to hold the result rows */ /* Build tuplestore to hold the result rows */
InitMaterializedSRF(fcinfo, 0); InitMaterializedSRF(fcinfo, 0);
@ -67,9 +67,10 @@ pg_get_wait_events(PG_FUNCTION_ARGS)
} }
/* Handle custom wait events for extensions */ /* Handle custom wait events for extensions */
waiteventnames = GetWaitEventExtensionNames(&nbextwaitevents); waiteventnames = GetWaitEventCustomNames(PG_WAIT_EXTENSION,
&nbwaitevents);
for (int idx = 0; idx < nbextwaitevents; idx++) for (int idx = 0; idx < nbwaitevents; idx++)
{ {
StringInfoData buf; StringInfoData buf;
Datum values[PG_GET_WAIT_EVENTS_COLS] = {0}; Datum values[PG_GET_WAIT_EVENTS_COLS] = {0};
@ -89,5 +90,29 @@ pg_get_wait_events(PG_FUNCTION_ARGS)
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls); tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
} }
/* Likewise for injection points */
waiteventnames = GetWaitEventCustomNames(PG_WAIT_INJECTIONPOINT,
&nbwaitevents);
for (int idx = 0; idx < nbwaitevents; idx++)
{
StringInfoData buf;
Datum values[PG_GET_WAIT_EVENTS_COLS] = {0};
bool nulls[PG_GET_WAIT_EVENTS_COLS] = {0};
values[0] = CStringGetTextDatum("InjectionPoint");
values[1] = CStringGetTextDatum(waiteventnames[idx]);
initStringInfo(&buf);
appendStringInfo(&buf,
"Waiting for injection point \"%s\"",
waiteventnames[idx]);
values[2] = CStringGetTextDatum(buf.data);
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
return (Datum) 0; return (Datum) 0;
} }

View File

@ -340,7 +340,7 @@ LogicalRepWorker "Waiting to read or update the state of logical replication wor
XactTruncation "Waiting to execute <function>pg_xact_status</function> or update the oldest transaction ID available to it." XactTruncation "Waiting to execute <function>pg_xact_status</function> or update the oldest transaction ID available to it."
WrapLimitsVacuum "Waiting to update limits on transaction id and multixact consumption." WrapLimitsVacuum "Waiting to update limits on transaction id and multixact consumption."
NotifyQueueTail "Waiting to update limit on <command>NOTIFY</command> message storage." NotifyQueueTail "Waiting to update limit on <command>NOTIFY</command> message storage."
WaitEventExtension "Waiting to read or update custom wait events information for extensions." WaitEventCustom "Waiting to read or update custom wait events information."
WALSummarizer "Waiting to read or update WAL summarization state." WALSummarizer "Waiting to read or update WAL summarization state."
DSMRegistry "Waiting to read or update the dynamic shared memory registry." DSMRegistry "Waiting to read or update the dynamic shared memory registry."
InjectionPoint "Waiting to read or update information related to injection points." InjectionPoint "Waiting to read or update information related to injection points."

View File

@ -78,7 +78,7 @@ PG_LWLOCK(44, XactTruncation)
/* 45 was XactTruncationLock until removal of BackendRandomLock */ /* 45 was XactTruncationLock until removal of BackendRandomLock */
PG_LWLOCK(46, WrapLimitsVacuum) PG_LWLOCK(46, WrapLimitsVacuum)
PG_LWLOCK(47, NotifyQueueTail) PG_LWLOCK(47, NotifyQueueTail)
PG_LWLOCK(48, WaitEventExtension) PG_LWLOCK(48, WaitEventCustom)
PG_LWLOCK(49, WALSummarizer) PG_LWLOCK(49, WALSummarizer)
PG_LWLOCK(50, DSMRegistry) PG_LWLOCK(50, DSMRegistry)
PG_LWLOCK(51, InjectionPoint) PG_LWLOCK(51, InjectionPoint)

View File

@ -24,6 +24,7 @@
#define PG_WAIT_IPC 0x08000000U #define PG_WAIT_IPC 0x08000000U
#define PG_WAIT_TIMEOUT 0x09000000U #define PG_WAIT_TIMEOUT 0x09000000U
#define PG_WAIT_IO 0x0A000000U #define PG_WAIT_IO 0x0A000000U
#define PG_WAIT_INJECTIONPOINT 0x0B000000U
/* enums for wait events */ /* enums for wait events */
#include "utils/wait_event_types.h" #include "utils/wait_event_types.h"
@ -38,26 +39,28 @@ extern void pgstat_reset_wait_event_storage(void);
extern PGDLLIMPORT uint32 *my_wait_event_info; extern PGDLLIMPORT uint32 *my_wait_event_info;
/* ---------- /*
* Wait Events - Extension * Wait Events - Extension, InjectionPoint
* *
* Use this category when the server process is waiting for some condition * Use InjectionPoint when the server process is waiting in an injection
* defined by an extension module. * point. Use Extension for other cases of the server process waiting for
* some condition defined by an extension module.
* *
* Extensions can define their own wait events in this category. They should * Extensions can define their own wait events in these categories. They
* call WaitEventExtensionNew() with a wait event string. If the wait event * should call one of these functions with a wait event string. If the wait
* associated to a string is already allocated, it returns the wait event * event associated to a string is already allocated, it returns the wait
* information to use. If not, it gets one wait event ID allocated from * event information to use. If not, it gets one wait event ID allocated from
* a shared counter, associates the string to the ID in the shared dynamic * a shared counter, associates the string to the ID in the shared dynamic
* hash and returns the wait event information. * hash and returns the wait event information.
* *
* The ID retrieved can be used with pgstat_report_wait_start() or equivalent. * The ID retrieved can be used with pgstat_report_wait_start() or equivalent.
*/ */
extern void WaitEventExtensionShmemInit(void);
extern Size WaitEventExtensionShmemSize(void);
extern uint32 WaitEventExtensionNew(const char *wait_event_name); extern uint32 WaitEventExtensionNew(const char *wait_event_name);
extern char **GetWaitEventExtensionNames(int *nwaitevents); extern uint32 WaitEventInjectionPointNew(const char *wait_event_name);
extern void WaitEventCustomShmemInit(void);
extern Size WaitEventCustomShmemSize(void);
extern char **GetWaitEventCustomNames(uint32 classId, int *nwaitevents);
/* ---------- /* ----------
* pgstat_report_wait_start() - * pgstat_report_wait_start() -

View File

@ -216,7 +216,7 @@ injection_wait(const char *name, const void *private_data)
* this custom wait event name is not released, but we don't care much for * this custom wait event name is not released, but we don't care much for
* testing as this should be short-lived. * testing as this should be short-lived.
*/ */
injection_wait_event = WaitEventExtensionNew(name); injection_wait_event = WaitEventInjectionPointNew(name);
/* /*
* Find a free slot to wait for, and register this injection point's name. * Find a free slot to wait for, and register this injection point's name.

View File

@ -158,9 +158,10 @@ select name, setting from pg_settings where name like 'enable%';
enable_tidscan | on enable_tidscan | on
(22 rows) (22 rows)
-- There are always wait event descriptions for various types. -- There are always wait event descriptions for various types. InjectionPoint
-- may be present or absent, depending on history since last postmaster start.
select type, count(*) > 0 as ok FROM pg_wait_events select type, count(*) > 0 as ok FROM pg_wait_events
group by type order by type COLLATE "C"; where type <> 'InjectionPoint' group by type order by type COLLATE "C";
type | ok type | ok
-----------+---- -----------+----
Activity | t Activity | t

View File

@ -70,9 +70,10 @@ select count(*) = 0 as ok from pg_stat_wal_receiver;
-- a regression test run. -- a regression test run.
select name, setting from pg_settings where name like 'enable%'; select name, setting from pg_settings where name like 'enable%';
-- There are always wait event descriptions for various types. -- There are always wait event descriptions for various types. InjectionPoint
-- may be present or absent, depending on history since last postmaster start.
select type, count(*) > 0 as ok FROM pg_wait_events select type, count(*) > 0 as ok FROM pg_wait_events
group by type order by type COLLATE "C"; where type <> 'InjectionPoint' group by type order by type COLLATE "C";
-- Test that the pg_timezone_names and pg_timezone_abbrevs views are -- Test that the pg_timezone_names and pg_timezone_abbrevs views are
-- more-or-less working. We can't test their contents in any great detail -- more-or-less working. We can't test their contents in any great detail

View File

@ -3099,9 +3099,9 @@ WaitEvent
WaitEventActivity WaitEventActivity
WaitEventBufferPin WaitEventBufferPin
WaitEventClient WaitEventClient
WaitEventExtensionCounterData WaitEventCustomCounterData
WaitEventExtensionEntryById WaitEventCustomEntryByInfo
WaitEventExtensionEntryByName WaitEventCustomEntryByName
WaitEventIO WaitEventIO
WaitEventIPC WaitEventIPC
WaitEventSet WaitEventSet