mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-11-21 03:13:05 +08:00
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:
parent
0844b39689
commit
bb93640a68
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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');
|
||||||
|
|
||||||
|
@ -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:
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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."
|
||||||
|
@ -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)
|
||||||
|
@ -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() -
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -3099,9 +3099,9 @@ WaitEvent
|
|||||||
WaitEventActivity
|
WaitEventActivity
|
||||||
WaitEventBufferPin
|
WaitEventBufferPin
|
||||||
WaitEventClient
|
WaitEventClient
|
||||||
WaitEventExtensionCounterData
|
WaitEventCustomCounterData
|
||||||
WaitEventExtensionEntryById
|
WaitEventCustomEntryByInfo
|
||||||
WaitEventExtensionEntryByName
|
WaitEventCustomEntryByName
|
||||||
WaitEventIO
|
WaitEventIO
|
||||||
WaitEventIPC
|
WaitEventIPC
|
||||||
WaitEventSet
|
WaitEventSet
|
||||||
|
Loading…
Reference in New Issue
Block a user