From 876c7e98bd8fae0c6edd15abb75462bdee777d67 Mon Sep 17 00:00:00 2001 From: Oleg Tselebrovskiy Date: Thu, 6 Aug 2026 13:14:47 +0700 Subject: [PATCH 1/2] Fix collector hangs if requesting backend is gone If a backend tried to access non-existent collector it got an error, but it has already set a request. When the collector restarts it will see this request and will start waiting for non-existant backend We fix this by checking collector's existence first and starting IPC iff collector exists --- pg_wait_sampling.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/pg_wait_sampling.c b/pg_wait_sampling.c index 0afb24f..d9e4b39 100644 --- a/pg_wait_sampling.c +++ b/pg_wait_sampling.c @@ -654,6 +654,16 @@ receive_array(SHMRequest request, Size item_size, Size *count) char *ptr; MemoryContext oldctx; + /* Check that the collector was started to avoid NULL pointer dereference */ + if (!pgws_collector_hdr->latch) + ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("pg_wait_sampling collector wasn't started"))); + + /* Check that the collector exists to avoid getting stuck in shm_mq_receive */ + if (pgws_collector_hdr->latch->owner_pid == 0) + ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("pg_wait_sampling collector doesn't exist"))); + /* Ensure nobody else trying to send request to queue */ pgws_init_lock_tag(&queueTag, PGWS_QUEUE_LOCK); LockAcquire(&queueTag, ExclusiveLock, false, false); @@ -664,19 +674,6 @@ receive_array(SHMRequest request, Size item_size, Size *count) pgws_collector_hdr->request = request; LockRelease(&collectorTag, ExclusiveLock, false); - /* - * Check that the collector was started to avoid NULL - * pointer dereference. - */ - if (!pgws_collector_hdr->latch) - ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), - errmsg("pg_wait_sampling collector wasn't started"))); - - /* Check that the collector exists to avoid getting stuck in shm_mq_receive */ - if (pgws_collector_hdr->latch->owner_pid == 0) - ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), - errmsg("pg_wait_sampling collector doesn't exist"))); - SetLatch(pgws_collector_hdr->latch); shm_mq_set_receiver(recv_mq, MyProc); @@ -837,6 +834,16 @@ pg_wait_sampling_reset_profile(PG_FUNCTION_ARGS) check_shmem(); + /* Check that the collector was started to avoid NULL pointer dereference */ + if (!pgws_collector_hdr->latch) + ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("pg_wait_sampling collector wasn't started"))); + + /* Check that the collector exists to avoid setting pgws_collector_hdr->request */ + if (pgws_collector_hdr->latch->owner_pid == 0) + ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("pg_wait_sampling collector doesn't exist"))); + pgws_init_lock_tag(&queueTag, PGWS_QUEUE_LOCK); LockAcquire(&queueTag, ExclusiveLock, false, false); @@ -846,14 +853,6 @@ pg_wait_sampling_reset_profile(PG_FUNCTION_ARGS) pgws_collector_hdr->request = PROFILE_RESET; LockRelease(&collectorTag, ExclusiveLock, false); - /* - * Check that the collector was started to avoid NULL - * pointer dereference. - */ - if (!pgws_collector_hdr->latch) - ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), - errmsg("pg_wait_sampling collector wasn't started"))); - SetLatch(pgws_collector_hdr->latch); LockRelease(&queueTag, ExclusiveLock, false); From 2c22a04ca3f03f299d73e4be887f653941204a15 Mon Sep 17 00:00:00 2001 From: Oleg Tselebrovskiy Date: Thu, 6 Aug 2026 14:01:42 +0700 Subject: [PATCH 2/2] Remove PG-13 support Also update copyright --- README.md | 6 ++--- pg_wait_sampling.c | 56 +++++++++------------------------------------- 2 files changed, 14 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 5b8e916..61151cd 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ PostgreSQL installation. It is available from [github](https://github.com/postgrespro/pg_wait_sampling) under the same license as [PostgreSQL](http://www.postgresql.org/about/licence/) -and supports PostgreSQL 13+. +and supports PostgreSQL 14+. Installation ------------ @@ -62,10 +62,10 @@ repository: https://download.postgresql.org/pub/repos/ Manual build ------------ -`pg_wait_sampling` is PostgreSQL extension which requires PostgreSQL 13 or +`pg_wait_sampling` is PostgreSQL extension which requires PostgreSQL 14 or higher. Before build and install you should ensure following: - * PostgreSQL version is 13 or higher. + * PostgreSQL version is 14 or higher. * You have development package of PostgreSQL installed or you built PostgreSQL from source. * Your PATH variable is configured so that `pg_config` command available, or diff --git a/pg_wait_sampling.c b/pg_wait_sampling.c index d9e4b39..dc0badc 100644 --- a/pg_wait_sampling.c +++ b/pg_wait_sampling.c @@ -2,7 +2,7 @@ * pg_wait_sampling.c * Track information about wait events. * - * Copyright (c) 2015-2025, Postgres Professional + * Copyright (c) 2015-2026, Postgres Professional * * IDENTIFICATION * contrib/pg_wait_sampling/pg_wait_sampling.c @@ -75,9 +75,7 @@ static shmem_request_hook_type prev_shmem_request_hook = NULL; static shmem_startup_hook_type prev_shmem_startup_hook = NULL; static PGPROC *search_proc(int backendPid); static PlannedStmt *pgws_planner_hook(Query *parse, -#if PG_VERSION_NUM >= 130000 const char *query_string, -#endif int cursorOptions, ParamListInfo boundParams #if PG_VERSION_NUM >= 190000 , ExplainState *es @@ -87,7 +85,7 @@ static void pgws_ExecutorStart(QueryDesc *queryDesc, int eflags); static void pgws_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count -#if PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 180000 +#if PG_VERSION_NUM < 180000 ,bool execute_once #endif ); @@ -95,18 +93,12 @@ static void pgws_ExecutorFinish(QueryDesc *queryDesc); static void pgws_ExecutorEnd(QueryDesc *queryDesc); static void pgws_ProcessUtility(PlannedStmt *pstmt, const char *queryString, -#if PG_VERSION_NUM >= 140000 bool readOnlyTree, -#endif ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, -#if PG_VERSION_NUM >= 130000 QueryCompletion *qc -#else - char *completionTag -#endif ); /*---- GUC variables ----*/ @@ -958,9 +950,7 @@ pg_wait_sampling_get_history(PG_FUNCTION_ARGS) */ static PlannedStmt * pgws_planner_hook(Query *parse, -#if PG_VERSION_NUM >= 130000 const char *query_string, -#endif int cursorOptions, ParamListInfo boundParams #if PG_VERSION_NUM >= 190000 @@ -984,9 +974,7 @@ pgws_planner_hook(Query *parse, /* Invoke original hook if needed */ if (planner_hook_next) result = planner_hook_next(parse, -#if PG_VERSION_NUM >= 130000 query_string, -#endif cursorOptions, boundParams #if PG_VERSION_NUM >= 190000 , es @@ -994,9 +982,7 @@ pgws_planner_hook(Query *parse, ); else result = standard_planner(parse, -#if PG_VERSION_NUM >= 130000 query_string, -#endif cursorOptions, boundParams #if PG_VERSION_NUM >= 190000 , es @@ -1042,7 +1028,7 @@ static void pgws_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count -#if PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 180000 +#if PG_VERSION_NUM < 180000 ,bool execute_once #endif ) @@ -1054,16 +1040,16 @@ pgws_ExecutorRun(QueryDesc *queryDesc, PG_TRY(); { if (prev_ExecutorRun) -#if PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 180000 - prev_ExecutorRun(queryDesc, direction, count, execute_once); -#else +#if PG_VERSION_NUM >= 180000 prev_ExecutorRun(queryDesc, direction, count); +#else + prev_ExecutorRun(queryDesc, direction, count, execute_once); #endif else -#if PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 180000 - standard_ExecutorRun(queryDesc, direction, count, execute_once); -#else +#if PG_VERSION_NUM >= 180000 standard_ExecutorRun(queryDesc, direction, count); +#else + standard_ExecutorRun(queryDesc, direction, count, execute_once); #endif nesting_level--; if (nesting_level == 0) @@ -1131,18 +1117,12 @@ pgws_ExecutorEnd(QueryDesc *queryDesc) static void pgws_ProcessUtility(PlannedStmt *pstmt, const char *queryString, -#if PG_VERSION_NUM >= 140000 bool readOnlyTree, -#endif ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, -#if PG_VERSION_NUM >= 130000 QueryCompletion *qc -#else - char *completionTag -#endif ) { int i = MyProc - ProcGlobal->allProcs; @@ -1159,30 +1139,16 @@ pgws_ProcessUtility(PlannedStmt *pstmt, { if (prev_ProcessUtility) prev_ProcessUtility(pstmt, queryString, -#if PG_VERSION_NUM >= 140000 readOnlyTree, -#endif context, params, queryEnv, dest, -#if PG_VERSION_NUM >= 130000 - qc -#else - completionTag -#endif - ); + qc); else standard_ProcessUtility(pstmt, queryString, -#if PG_VERSION_NUM >= 140000 readOnlyTree, -#endif context, params, queryEnv, dest, -#if PG_VERSION_NUM >= 130000 - qc -#else - completionTag -#endif - ); + qc); nesting_level--; if (nesting_level == 0) pgws_proc_queryids[i] = UINT64CONST(0);