From 0dbce8228aadacbebcd8475450ca53902ddcd18d Mon Sep 17 00:00:00 2001 From: Dmitry Fomin Date: Tue, 9 Jun 2026 16:35:24 +0000 Subject: [PATCH v1 3/7] wait_event_timing: expose overflow counters and add reset functions Surface the per-backend truncation counters maintained by the recording path, and add the ability to reset wait-event-timing statistics. pg_stat_get_wait_event_timing_overflow(pid) and the pg_stat_wait_event_timing_overflow view report, per backend, lwlock_overflow_count (LWLock waits dropped because the per-backend tranche hash was full), flat_overflow_count (events whose class index was out of range), and reset_count. Resets use a lock-free request/response so the hot path stays single writer: each slot carries an atomic reset_generation, bumped by the resetter; the owning backend compares it against a backend-local last-seen value at its next wait_end and clears its own counters, incrementing reset_count. pg_stat_reset_wait_event_timing(pid) resets one backend -- synchronously when it targets the caller's own session (any user; pid defaults to NULL), or asynchronously for another backend (requiring pg_signal_backend, matching pg_stat_reset_backend_stats). pg_stat_reset_wait_event_timing_all() resets every backend and is superuser-only. Builds without --enable-wait-event-timing keep empty-result/feature-not- supported stubs for the new functions. --- doc/src/sgml/config.sgml | 14 +- doc/src/sgml/monitoring.sgml | 178 +++++++++++++ src/backend/catalog/system_views.sql | 17 ++ .../utils/activity/wait_event_timing.c | 242 ++++++++++++++++++ src/include/catalog/pg_proc.dat | 23 ++ src/include/utils/wait_event_timing.h | 21 +- src/test/regress/expected/rules.out | 7 + .../regress/expected/wait_event_timing.out | 51 ++++ .../regress/expected/wait_event_timing_1.out | 40 +++ src/test/regress/sql/wait_event_timing.sql | 25 ++ 10 files changed, 611 insertions(+), 7 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 43c9d395cf5..15131fd9b7f 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -9188,13 +9188,17 @@ GRANT SET ON PARAMETER wait_event_capture TO pg_monitor; Sets the maximum number of distinct LWLock tranches whose timing is recorded individually per backend. PostgreSQL maintains a per-backend hash table mapping each tranche the backend encounters - to its histogram; once the table fills, further tranches are not - individually timed. Sized at server start; this parameter has no - effect on builds compiled without + to its histogram; once the table fills, further tranches are + counted against lwlock_overflow_count in + + pg_stat_wait_event_timing_overflow + and not individually timed. Sized at server start; this parameter + has no effect on builds compiled without . The default is 192; raise it if your installation loads many - extensions that register their own LWLock tranches. This parameter - can only be set at server start. + extensions that register their own LWLock tranches and you observe + a non-zero lwlock_overflow_count. This + parameter can only be set at server start. diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 8c4a523f971..34676e43eae 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -570,6 +570,15 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser + + pg_stat_wait_event_timing_overflowpg_stat_wait_event_timing_overflow + Per-backend truncation and reset counters for the wait-event + timing subsystem. See + + pg_stat_wait_event_timing_overflow for details. + + + @@ -4098,6 +4107,124 @@ ORDER BY b.bucket_idx; + + <structname>pg_stat_wait_event_timing_overflow</structname> + + + pg_stat_wait_event_timing_overflow + + + + The pg_stat_wait_event_timing_overflow view + exposes per-backend truncation counters for the wait-event timing + subsystem. Each backend owns a bounded LWLock timing hash + ( tranches) and a bounded flat event array; events that cannot + be mapped to a slot are counted here. A non-zero value means the + corresponding row(s) in + + pg_stat_wait_event_timing + are incomplete for that backend. Requires the server to be + compiled with . + + + + <structname>pg_stat_wait_event_timing_overflow</structname> View + + + + + Column Type + + + Description + + + + + + + + pid integer + + + Process ID of the backend + + + + + + backend_type text + + + Type of the backend (e.g. client backend, + checkpointer, walwriter) + + + + + + procnumber integer + + + Internal slot number (0-based process number) of the backend. + + + + + + lwlock_overflow_count bigint + + + Number of LWLock wait events dropped because the per-backend + LWLock timing hash was already full (more distinct tranches + observed in this session than + allows). + Zero means no LWLock truncation. A one-time + WARNING is also emitted to the server log on + first overflow. If you see this counter rising, raise + wait_event_timing_max_tranches at server + start (the per-backend memory cost is proportional and + described under that GUC). + + + + + + flat_overflow_count bigint + + + Number of non-LWLock wait events dropped because the event + could not be mapped to a known class / index. This almost + always indicates a code path emitting a wait event of a class + the timing infrastructure was not compiled for; it should be + zero in supported builds. + + + + + + reset_count bigint + + + Number of resets this backend has observed and acted + on; not a request counter. Own-backend resets via + pg_stat_reset_wait_event_timing(NULL) (or + passing the caller's own PID) are synchronous and bump this + column once per call. Cross-backend reset requests + coalesce: if several + pg_stat_reset_wait_event_timing(pid) + calls land between two of the target's wait events, the target + observes them as a single reset and increments + reset_count only once. Callers + polling for asynchronous-reset acknowledgment should watch for + any N → N+1 transition. + + + + +
+
+ <structname>pg_stat_database</structname> @@ -6154,6 +6281,57 @@ ORDER BY b.bucket_idx;
+ + + + pg_stat_reset_wait_event_timing + + pg_stat_reset_wait_event_timing ( pid integer DEFAULT NULL ) + void + + + Resets wait event timing counters for a single backend, identified + by its process ID (see pid in + + pg_stat_activity). + Passing NULL (or the caller's own + pg_backend_pid()) resets the current session; + any user may do this. Passing any other PID resets that backend + and requires membership in the + pg_signal_backend + role — the same role required by + pg_stat_reset_backend_stats, + pg_terminate_backend, and + pg_cancel_backend. Unknown or + already-exited PIDs are silent no-ops, matching the behavior of + pg_stat_reset_backend_stats. + + + + + + + pg_stat_reset_wait_event_timing_all + + pg_stat_reset_wait_event_timing_all () + void + + + Resets wait event timing counters for every backend in the + cluster. Requires superuser. This is intentionally stricter + than the per-backend variant + pg_stat_reset_wait_event_timing(pid), + which only requires pg_signal_backend: the + cluster-wide form has unbounded blast radius (it affects every + backend in a single call) and would erase forensic patterns + that span multiple backends, so it is gated to the cluster + owner. Returns before the resets have been observed by their + target backends; callers that need strict read-after-reset + semantics should poll each target's + reset_count column. + + + diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index b41661eb5d2..96aba833895 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -1630,3 +1630,20 @@ CREATE VIEW pg_stat_wait_event_timing AS FROM pg_stat_get_wait_event_timing(NULL) t; REVOKE ALL ON pg_stat_wait_event_timing FROM PUBLIC; GRANT SELECT ON pg_stat_wait_event_timing TO pg_read_all_stats; + +CREATE VIEW pg_stat_wait_event_timing_overflow AS + SELECT + t.pid, + t.backend_type, + t.procnumber, + t.lwlock_overflow_count, + t.flat_overflow_count, + t.reset_count + FROM pg_stat_get_wait_event_timing_overflow(NULL) t; +REVOKE ALL ON pg_stat_wait_event_timing_overflow FROM PUBLIC; +GRANT SELECT ON pg_stat_wait_event_timing_overflow TO pg_read_all_stats; + +-- Cluster-scope operations: revoked from PUBLIC (administrators can +-- delegate with GRANT EXECUTE); not granted to pg_read_all_stats because +-- they mutate state rather than read it. +REVOKE EXECUTE ON FUNCTION pg_stat_reset_wait_event_timing_all() FROM PUBLIC; diff --git a/src/backend/utils/activity/wait_event_timing.c b/src/backend/utils/activity/wait_event_timing.c index 44c43066ca7..2ec7d246399 100644 --- a/src/backend/utils/activity/wait_event_timing.c +++ b/src/backend/utils/activity/wait_event_timing.c @@ -60,6 +60,9 @@ const struct config_enum_entry wait_event_capture_options[] = { #include "funcapi.h" Datum pg_stat_get_wait_event_timing(PG_FUNCTION_ARGS); +Datum pg_stat_get_wait_event_timing_overflow(PG_FUNCTION_ARGS); +Datum pg_stat_reset_wait_event_timing(PG_FUNCTION_ARGS); +Datum pg_stat_reset_wait_event_timing_all(PG_FUNCTION_ARGS); Datum pg_stat_get_wait_event_timing(PG_FUNCTION_ARGS) @@ -68,6 +71,33 @@ pg_stat_get_wait_event_timing(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } +Datum +pg_stat_get_wait_event_timing_overflow(PG_FUNCTION_ARGS) +{ + InitMaterializedSRF(fcinfo, 0); + PG_RETURN_VOID(); +} + +Datum +pg_stat_reset_wait_event_timing(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("wait event capture is not supported by this build"), + errhint("Compile PostgreSQL with --enable-wait-event-timing."))); + PG_RETURN_VOID(); +} + +Datum +pg_stat_reset_wait_event_timing_all(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("wait event capture is not supported by this build"), + errhint("Compile PostgreSQL with --enable-wait-event-timing."))); + PG_RETURN_VOID(); +} + /* * GUC check hook for the stub build. Any value other than 'off' is * meaningless without --enable-wait-event-timing, so reject it -- or @@ -124,6 +154,7 @@ pgstat_reset_wait_event_timing_storage(void) #include "funcapi.h" #include "miscadmin.h" #include "port/pg_bitutils.h" +#include "storage/latch.h" #include "storage/proc.h" #include "storage/procarray.h" #include "storage/procnumber.h" @@ -143,6 +174,14 @@ pgstat_reset_wait_event_timing_storage(void) /* Pointer to this backend's timing state in shared memory. */ WaitEventTimingState *my_wait_event_timing = NULL; +/* + * Backend-local copy of the last reset generation this backend acted on. + * Compared against the shared reset_generation at every wait_end; when they + * differ, the owning backend performs the reset of its own counters on + * behalf of whoever called pg_stat_reset_wait_event_timing(target). + */ +static uint32 my_last_reset_generation = 0; + /* * Backend-local cached pointer to the start of the shared slot array, set * at shmem init (postmaster) and, in EXEC_BACKEND mode, at attach. Slots @@ -406,6 +445,7 @@ WaitEventTimingShmemInit(void *arg) LWLockTimingHashEntry *entries; int j; + pg_atomic_init_u32(&slot->reset_generation, 0); slot->lwlock_hash.num_used = 0; slot->lwlock_hash.hash_size = wait_event_timing_hash_size; slot->lwlock_hash.max_entries = wait_event_timing_max_entries; @@ -448,9 +488,17 @@ pgstat_set_wait_event_timing_storage(int procNumber) lwlock_timing_hash_clear(slot); slot->lwlock_overflow_count = 0; slot->flat_overflow_count = 0; + slot->reset_count = 0; slot->current_event = 0; INSTR_TIME_SET_ZERO(slot->wait_start); + /* + * Adopt the current shared reset generation as our baseline; the + * reset_generation counter persists across slot reuse, so a new backend + * must not treat the prior occupant's resets as its own. + */ + my_last_reset_generation = pg_atomic_read_u32(&slot->reset_generation); + /* Publish only after the slot is fully initialised. */ my_wait_event_timing = slot; } @@ -523,6 +571,7 @@ void pgstat_report_wait_end_timing(int capture_level) { uint32 event; + uint32 cur_reset_gen; (void) capture_level; @@ -531,6 +580,27 @@ pgstat_report_wait_end_timing(int capture_level) event = my_wait_event_timing->current_event; + /* + * Service a pending cross-backend reset request. A single relaxed atomic + * load; when the shared generation has advanced past the value we last + * acted on, clear our own counters on behalf of the requester and record + * the reset. wait_start is left untouched so the in-flight measurement + * still lands (in the freshly-zeroed counters), and current_event is + * zeroed so external readers do not see stale state. + */ + cur_reset_gen = pg_atomic_read_u32(&my_wait_event_timing->reset_generation); + if (cur_reset_gen != my_last_reset_generation) + { + memset(my_wait_event_timing->events, 0, + sizeof(my_wait_event_timing->events)); + lwlock_timing_hash_clear(my_wait_event_timing); + my_wait_event_timing->reset_count++; + my_wait_event_timing->lwlock_overflow_count = 0; + my_wait_event_timing->flat_overflow_count = 0; + my_wait_event_timing->current_event = 0; + my_last_reset_generation = cur_reset_gen; + } + if (event != 0 && !INSTR_TIME_IS_ZERO(my_wait_event_timing->wait_start)) { instr_time now; @@ -773,4 +843,176 @@ pg_stat_get_wait_event_timing(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } +/* + * SQL function: pg_stat_get_wait_event_timing_overflow(pid int4, OUT ...) + * + * Exposes the per-backend truncation counters that the recording path + * maintains: lwlock_overflow_count (LWLock waits dropped because the + * per-backend tranche hash was full), flat_overflow_count (events whose + * class index was out of range), and reset_count (resets the backend has + * observed and acted on). pid has the same optional semantics as + * pg_stat_get_wait_event_timing(). + */ +Datum +pg_stat_get_wait_event_timing_overflow(PG_FUNCTION_ARGS) +{ + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + int start_idx; + int end_idx; + int backend_idx; + + InitMaterializedSRF(fcinfo, 0); + + if (WaitEventTimingArray == NULL) + PG_RETURN_VOID(); + + if (!wait_event_timing_pid_range(fcinfo, &start_idx, &end_idx)) + PG_RETURN_VOID(); + + for (backend_idx = start_idx; backend_idx < end_idx; backend_idx++) + { + WaitEventTimingState *state = wet_slot(backend_idx); + PgBackendStatus *beentry; + Datum values[6]; + bool nulls[6]; + + beentry = pgstat_get_beentry_by_proc_number(backend_idx); + if (beentry == NULL) + continue; + if (!HAS_PGSTAT_PERMISSIONS(beentry->st_userid)) + continue; + + memset(nulls, 0, sizeof(nulls)); + + values[0] = Int32GetDatum(beentry->st_procpid); + values[1] = CStringGetTextDatum(GetBackendTypeDesc(beentry->st_backendType)); + values[2] = Int32GetDatum(backend_idx); + values[3] = Int64GetDatum(state->lwlock_overflow_count); + values[4] = Int64GetDatum(state->flat_overflow_count); + values[5] = Int64GetDatum(state->reset_count); + + tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls); + } + + PG_RETURN_VOID(); +} + +/* + * Request a cross-backend self-reset on the given slot: bump the slot's + * reset_generation and wake the target so it promptly observes the change + * and clears its own counters at its next wait_end. Lock-free: only the + * owning backend ever writes its statistics. + */ +static void +wait_event_timing_request_reset(int slot_idx) +{ + Assert(slot_idx >= 0 && slot_idx < NUM_WAIT_EVENT_TIMING_SLOTS); + + if (WaitEventTimingArray == NULL) + return; + + pg_atomic_fetch_add_u32(&wet_slot(slot_idx)->reset_generation, 1); + + /* + * The slot index is also the PGPROC array index. Waking the target + * shortens the time before it completes its current wait and notices the + * request; setting a latch on a slot with no live owner is harmless. + */ + if (ProcGlobal != NULL && ProcGlobal->allProcs != NULL) + SetLatch(&ProcGlobal->allProcs[slot_idx].procLatch); +} + +/* + * SQL function: pg_stat_reset_wait_event_timing(pid int4) + * + * NULL or own pid : reset the caller's own counters synchronously. + * another pid : request a cross-backend reset (pg_signal_backend). + * unknown pid : silent no-op. + * + * Cross-backend resets are asynchronous: the target clears its counters at + * its next wait_end. Callers needing read-after-reset semantics should + * target their own backend, or poll reset_count in + * pg_stat_wait_event_timing_overflow until it increments. + */ +Datum +pg_stat_reset_wait_event_timing(PG_FUNCTION_ARGS) +{ + int target_pid; + PGPROC *proc; + int procNumber; + + if (PG_ARGISNULL(0) || PG_GETARG_INT32(0) == MyProcPid) + { + /* + * Own backend: synchronous, no lock needed (single writer). + * wait_start is already zero (every wait_end zeroes it and we cannot + * be mid-wait while running this function), so there is no in-flight + * measurement to preserve. + */ + if (my_wait_event_timing != NULL) + { + memset(my_wait_event_timing->events, 0, + sizeof(my_wait_event_timing->events)); + lwlock_timing_hash_clear(my_wait_event_timing); + my_wait_event_timing->reset_count++; + my_wait_event_timing->lwlock_overflow_count = 0; + my_wait_event_timing->flat_overflow_count = 0; + my_wait_event_timing->current_event = 0; + } + PG_RETURN_VOID(); + } + + /* + * Cross-backend reset requires pg_signal_backend, matching + * pg_stat_reset_backend_stats(pid): anyone who can terminate the target + * backend can already destroy more forensic state than a counter wipe. + */ + if (!has_privs_of_role(GetUserId(), ROLE_PG_SIGNAL_BACKEND)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to reset another backend's wait event timing"), + errdetail("Only roles with privileges of the \"pg_signal_backend\" role may reset another backend's wait event timing."))); + + target_pid = PG_GETARG_INT32(0); + + proc = BackendPidGetProc(target_pid); + if (proc == NULL) + proc = AuxiliaryPidGetProc(target_pid); + if (proc == NULL) + PG_RETURN_VOID(); /* unknown/dead pid: silent no-op */ + + procNumber = GetNumberFromPGProc(proc); + if (procNumber < 0 || procNumber >= NUM_WAIT_EVENT_TIMING_SLOTS) + PG_RETURN_VOID(); + + wait_event_timing_request_reset(procNumber); + + PG_RETURN_VOID(); +} + +/* + * SQL function: pg_stat_reset_wait_event_timing_all() + * + * Request a reset on every backend. Execution is revoked from PUBLIC by + * default (the blast radius is the whole cluster, a different decision + * from the per-backend variant); administrators can delegate with GRANT. + */ +Datum +pg_stat_reset_wait_event_timing_all(PG_FUNCTION_ARGS) +{ + int i; + + /* + * Execution is revoked from PUBLIC in system_views.sql; administrators + * can delegate with GRANT EXECUTE. + */ + if (WaitEventTimingArray == NULL) + PG_RETURN_VOID(); + + for (i = 0; i < NUM_WAIT_EVENT_TIMING_SLOTS; i++) + wait_event_timing_request_reset(i); + + PG_RETURN_VOID(); +} + #endif /* USE_WAIT_EVENT_TIMING */ diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 1b384af298b..9c5dad6d925 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -12728,4 +12728,27 @@ proargnames => '{pid,pid,backend_type,procnumber,wait_event_type,wait_event,calls,total_time_ms,avg_time_us,max_time_us,histogram}', prosrc => 'pg_stat_get_wait_event_timing' }, +{ oid => '9958', + descr => 'statistics: reset wait event timing counters for the given backend (NULL = own)', + proname => 'pg_stat_reset_wait_event_timing', proisstrict => 'f', + provolatile => 'v', prorettype => 'void', proargtypes => 'int4', + proargnames => '{pid}', proargdefaults => '{NULL}', + prosrc => 'pg_stat_reset_wait_event_timing' }, + +{ oid => '9959', + descr => 'statistics: per-backend wait event timing overflow counters (rows lost to LWLock hash / flat array overflow)', + proname => 'pg_stat_get_wait_event_timing_overflow', prorows => '1000', + proisstrict => 'f', proretset => 't', provolatile => 's', proparallel => 'r', + prorettype => 'record', proargtypes => 'int4', + proallargtypes => '{int4,int4,text,int4,int8,int8,int8}', + proargmodes => '{i,o,o,o,o,o,o}', + proargnames => '{pid,pid,backend_type,procnumber,lwlock_overflow_count,flat_overflow_count,reset_count}', + prosrc => 'pg_stat_get_wait_event_timing_overflow' }, + +{ oid => '9960', + descr => 'statistics: reset wait event timing counters for all backends (superuser only)', + proname => 'pg_stat_reset_wait_event_timing_all', + provolatile => 'v', prorettype => 'void', proargtypes => '', + prosrc => 'pg_stat_reset_wait_event_timing_all' }, + ] diff --git a/src/include/utils/wait_event_timing.h b/src/include/utils/wait_event_timing.h index e3f94fe87b1..22dfc1410fa 100644 --- a/src/include/utils/wait_event_timing.h +++ b/src/include/utils/wait_event_timing.h @@ -25,6 +25,7 @@ #ifndef WAIT_EVENT_TIMING_H #define WAIT_EVENT_TIMING_H +#include "port/atomics.h" #include "portability/instr_time.h" #include "storage/shmem.h" #include "utils/wait_event_types.h" @@ -137,12 +138,28 @@ extern PGDLLIMPORT int wait_event_timing_max_tranches; */ typedef struct WaitEventTimingState { + /* + * Generation counter for cross-backend reset requests. Bumped atomically + * by pg_stat_reset_wait_event_timing(target); the owning backend notices + * the change at its next wait_end and clears its own counters. This + * keeps the hot path lock-free: only the owning backend ever writes its + * statistics, so there is no writer/resetter race. + */ + pg_atomic_uint32 reset_generation; + /* Current wait start timestamp (set by pgstat_report_wait_start). */ instr_time wait_start; /* Current wait_event_info (cached for use in wait_end). */ uint32 current_event; + /* + * Number of resets this backend has observed and acted on. Own-backend + * resets are synchronous (one bump per call); cross-backend resets + * coalesce (multiple requests between two wait_ends count as one). + */ + int64 reset_count; + /* Per-event statistics: flat array for bounded classes. */ WaitEventTimingEntry events[WAIT_EVENT_TIMING_NUM_EVENTS]; @@ -151,8 +168,8 @@ typedef struct WaitEventTimingState /* * Count of LWLock events dropped because the LWLock-timing hash reached - * its cap (wait_event_timing_max_tranches). Written here; a later commit - * in the series exposes it via SQL. + * its cap (wait_event_timing_max_tranches). Exposed via + * pg_stat_wait_event_timing_overflow. */ int64 lwlock_overflow_count; diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 7375d411c81..71c95cb0746 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -2434,6 +2434,13 @@ pg_stat_wait_event_timing| SELECT pid, max_time_us, histogram FROM pg_stat_get_wait_event_timing(NULL::integer) t(pid, backend_type, procnumber, wait_event_type, wait_event, calls, total_time_ms, avg_time_us, max_time_us, histogram); +pg_stat_wait_event_timing_overflow| SELECT pid, + backend_type, + procnumber, + lwlock_overflow_count, + flat_overflow_count, + reset_count + FROM pg_stat_get_wait_event_timing_overflow(NULL::integer) t(pid, backend_type, procnumber, lwlock_overflow_count, flat_overflow_count, reset_count); pg_stat_wal| SELECT wal_records, wal_fpi, wal_bytes, diff --git a/src/test/regress/expected/wait_event_timing.out b/src/test/regress/expected/wait_event_timing.out index 925b315c4e8..8938ffe5fb0 100644 --- a/src/test/regress/expected/wait_event_timing.out +++ b/src/test/regress/expected/wait_event_timing.out @@ -81,4 +81,55 @@ FROM pg_stat_get_wait_event_timing(-1); 0 (1 row) +-- Overflow/reset counters for this backend. A simple test backend uses +-- few LWLock tranches and no out-of-range classes, so both overflow +-- counters are zero, and a fresh backend has not been reset. +SELECT lwlock_overflow_count, flat_overflow_count, reset_count +FROM pg_stat_wait_event_timing_overflow +WHERE pid = pg_backend_pid(); + lwlock_overflow_count | flat_overflow_count | reset_count +-----------------------+---------------------+------------- + 0 | 0 | 0 +(1 row) + +-- Resetting our own backend is synchronous: the PgSleep row is cleared and +-- reset_count advances. (We filter to PgSleep because inter-command waits +-- such as ClientRead may be recorded again before the next statement runs.) +SELECT pg_stat_reset_wait_event_timing(NULL); + pg_stat_reset_wait_event_timing +--------------------------------- + +(1 row) + +SELECT count(*) AS pgsleep_rows_after_reset +FROM pg_stat_wait_event_timing +WHERE pid = pg_backend_pid() AND wait_event = 'PgSleep'; + pgsleep_rows_after_reset +-------------------------- + 0 +(1 row) + +SELECT reset_count +FROM pg_stat_wait_event_timing_overflow +WHERE pid = pg_backend_pid(); + reset_count +------------- + 1 +(1 row) + +-- The pid argument defaults to NULL, so a no-argument call resets the +-- caller's own backend. +SELECT pg_stat_reset_wait_event_timing(); + pg_stat_reset_wait_event_timing +--------------------------------- + +(1 row) + +-- Resetting an unknown pid is a silent no-op, not an error. +SELECT pg_stat_reset_wait_event_timing(2147483647); + pg_stat_reset_wait_event_timing +--------------------------------- + +(1 row) + RESET wait_event_capture; diff --git a/src/test/regress/expected/wait_event_timing_1.out b/src/test/regress/expected/wait_event_timing_1.out index 038a8565657..3aad898d9bf 100644 --- a/src/test/regress/expected/wait_event_timing_1.out +++ b/src/test/regress/expected/wait_event_timing_1.out @@ -82,4 +82,44 @@ FROM pg_stat_get_wait_event_timing(-1); 0 (1 row) +-- Overflow/reset counters for this backend. A simple test backend uses +-- few LWLock tranches and no out-of-range classes, so both overflow +-- counters are zero, and a fresh backend has not been reset. +SELECT lwlock_overflow_count, flat_overflow_count, reset_count +FROM pg_stat_wait_event_timing_overflow +WHERE pid = pg_backend_pid(); + lwlock_overflow_count | flat_overflow_count | reset_count +-----------------------+---------------------+------------- +(0 rows) + +-- Resetting our own backend is synchronous: the PgSleep row is cleared and +-- reset_count advances. (We filter to PgSleep because inter-command waits +-- such as ClientRead may be recorded again before the next statement runs.) +SELECT pg_stat_reset_wait_event_timing(NULL); +ERROR: wait event capture is not supported by this build +HINT: Compile PostgreSQL with --enable-wait-event-timing. +SELECT count(*) AS pgsleep_rows_after_reset +FROM pg_stat_wait_event_timing +WHERE pid = pg_backend_pid() AND wait_event = 'PgSleep'; + pgsleep_rows_after_reset +-------------------------- + 0 +(1 row) + +SELECT reset_count +FROM pg_stat_wait_event_timing_overflow +WHERE pid = pg_backend_pid(); + reset_count +------------- +(0 rows) + +-- The pid argument defaults to NULL, so a no-argument call resets the +-- caller's own backend. +SELECT pg_stat_reset_wait_event_timing(); +ERROR: wait event capture is not supported by this build +HINT: Compile PostgreSQL with --enable-wait-event-timing. +-- Resetting an unknown pid is a silent no-op, not an error. +SELECT pg_stat_reset_wait_event_timing(2147483647); +ERROR: wait event capture is not supported by this build +HINT: Compile PostgreSQL with --enable-wait-event-timing. RESET wait_event_capture; diff --git a/src/test/regress/sql/wait_event_timing.sql b/src/test/regress/sql/wait_event_timing.sql index da15dbbe495..a30fcd155fd 100644 --- a/src/test/regress/sql/wait_event_timing.sql +++ b/src/test/regress/sql/wait_event_timing.sql @@ -51,4 +51,29 @@ WHERE pid = pg_backend_pid() AND wait_event = 'PgSleep'; SELECT count(*) AS rows_for_bogus_pid FROM pg_stat_get_wait_event_timing(-1); +-- Overflow/reset counters for this backend. A simple test backend uses +-- few LWLock tranches and no out-of-range classes, so both overflow +-- counters are zero, and a fresh backend has not been reset. +SELECT lwlock_overflow_count, flat_overflow_count, reset_count +FROM pg_stat_wait_event_timing_overflow +WHERE pid = pg_backend_pid(); + +-- Resetting our own backend is synchronous: the PgSleep row is cleared and +-- reset_count advances. (We filter to PgSleep because inter-command waits +-- such as ClientRead may be recorded again before the next statement runs.) +SELECT pg_stat_reset_wait_event_timing(NULL); +SELECT count(*) AS pgsleep_rows_after_reset +FROM pg_stat_wait_event_timing +WHERE pid = pg_backend_pid() AND wait_event = 'PgSleep'; +SELECT reset_count +FROM pg_stat_wait_event_timing_overflow +WHERE pid = pg_backend_pid(); + +-- The pid argument defaults to NULL, so a no-argument call resets the +-- caller's own backend. +SELECT pg_stat_reset_wait_event_timing(); + +-- Resetting an unknown pid is a silent no-op, not an error. +SELECT pg_stat_reset_wait_event_timing(2147483647); + RESET wait_event_capture; -- 2.43.0