Skip to content

Commit 12c0f7d

Browse files
authored
Track on CPU events too (#74)
To not count dead backends as still running on the CPU we need to detect that the backend is dead. Starting from PG17 proc->pid is reset in ProcKill, before this we can check if the process latch is disowned. Not nice to be poking around in latch internals like this, but all alternatives seem to involve scanning bestatus array and correlating pids. Also makes sense to exclude ourselves as we will always be on CPU while looking at wait events. Add a GUC for controlling whether on CPU events are counted.
1 parent 3fdca70 commit 12c0f7d

File tree

4 files changed

+60
-20
lines changed

4 files changed

+60
-20
lines changed

README.md

+12-7
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,14 @@ in-memory hash table.
133133
The work of wait event statistics collector worker is controlled by following
134134
GUCs.
135135

136-
| Parameter name | Data type | Description | Default value |
137-
| ----------------------------------- | --------- | ------------------------------------------- | ------------: |
138-
| pg_wait_sampling.history_size | int4 | Size of history in-memory ring buffer | 5000 |
139-
| pg_wait_sampling.history_period | int4 | Period for history sampling in milliseconds | 10 |
140-
| pg_wait_sampling.profile_period | int4 | Period for profile sampling in milliseconds | 10 |
141-
| pg_wait_sampling.profile_pid | bool | Whether profile should be per pid | true |
142-
| pg_wait_sampling.profile_queries | bool | Whether profile should be per query | true |
136+
| Parameter name | Data type | Description | Default value |
137+
|----------------------------------| --------- |---------------------------------------------|--------------:|
138+
| pg_wait_sampling.history_size | int4 | Size of history in-memory ring buffer | 5000 |
139+
| pg_wait_sampling.history_period | int4 | Period for history sampling in milliseconds | 10 |
140+
| pg_wait_sampling.profile_period | int4 | Period for profile sampling in milliseconds | 10 |
141+
| pg_wait_sampling.profile_pid | bool | Whether profile should be per pid | true |
142+
| pg_wait_sampling.profile_queries | bool | Whether profile should be per query | true |
143+
| pg_wait_sampling.sample_cpu | bool | Whether on CPU backends should be sampled | true |
143144

144145
If `pg_wait_sampling.profile_pid` is set to false, sampling profile wouldn't be
145146
collected in per-process manner. In this case the value of pid could would
@@ -148,6 +149,10 @@ be always zero and corresponding row contain samples among all the processes.
148149
While `pg_wait_sampling.profile_queries` is set to false `queryid` field in
149150
views will be zero.
150151

152+
If `pg_wait_sampling.sample_cpu` is set to true then processes that are not
153+
waiting on anything are also sampled. The wait event columns for such processes
154+
will be NULL.
155+
151156
These GUCs are allowed to be changed by superuser. Also, they are placed into
152157
shared memory. Thus, they could be changed from any backend and affects worker
153158
runtime.

collector.c

+1-4
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,7 @@ probe_waits(History *observations, HTAB *profile_hash,
163163
*observation;
164164
PGPROC *proc = &ProcGlobal->allProcs[i];
165165

166-
if (proc->pid == 0)
167-
continue;
168-
169-
if (proc->wait_event_info == 0)
166+
if (!pgws_should_sample_proc(proc))
170167
continue;
171168

172169
/* Collect next wait event sample */

pg_wait_sampling.c

+45-9
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,8 @@ setup_gucs()
203203
history_period_found = false,
204204
profile_period_found = false,
205205
profile_pid_found = false,
206-
profile_queries_found = false;
206+
profile_queries_found = false,
207+
sample_cpu_found = false;
207208

208209
get_guc_variables_compat(&guc_vars, &numOpts);
209210

@@ -245,6 +246,12 @@ setup_gucs()
245246
var->_bool.variable = &pgws_collector_hdr->profileQueries;
246247
pgws_collector_hdr->profileQueries = true;
247248
}
249+
else if (!strcmp(name, "pg_wait_sampling.sample_cpu"))
250+
{
251+
sample_cpu_found = true;
252+
var->_bool.variable = &pgws_collector_hdr->sampleCpu;
253+
pgws_collector_hdr->sampleCpu = true;
254+
}
248255
}
249256

250257
if (!history_size_found)
@@ -277,11 +284,18 @@ setup_gucs()
277284
&pgws_collector_hdr->profileQueries, true,
278285
PGC_SUSET, 0, shmem_bool_guc_check_hook, NULL, NULL);
279286

287+
if (!sample_cpu_found)
288+
DefineCustomBoolVariable("pg_wait_sampling.sample_cpu",
289+
"Sets whether not waiting backends should be sampled.", NULL,
290+
&pgws_collector_hdr->sampleCpu, true,
291+
PGC_SUSET, 0, shmem_bool_guc_check_hook, NULL, NULL);
292+
280293
if (history_size_found
281294
|| history_period_found
282295
|| profile_period_found
283296
|| profile_pid_found
284-
|| profile_queries_found)
297+
|| profile_queries_found
298+
|| sample_cpu_found)
285299
{
286300
ProcessConfigFile(PGC_SIGHUP);
287301
}
@@ -438,6 +452,28 @@ search_proc(int pid)
438452
return NULL;
439453
}
440454

455+
/*
456+
* Decide whether this PGPROC entry should be included in profiles and output
457+
* views.
458+
*/
459+
bool
460+
pgws_should_sample_proc(PGPROC *proc)
461+
{
462+
if (proc->wait_event_info == 0 && !pgws_collector_hdr->sampleCpu)
463+
return false;
464+
465+
/*
466+
* On PostgreSQL versions < 17 the PGPROC->pid field is not reset on
467+
* process exit. This would lead to such processes getting counted for
468+
* null wait events. So instead we make use of DisownLatch() resetting
469+
* owner_pid during ProcKill().
470+
*/
471+
if (proc->pid == 0 || proc->procLatch.owner_pid == 0 || proc->pid == MyProcPid)
472+
return false;
473+
474+
return true;
475+
}
476+
441477
typedef struct
442478
{
443479
HistoryItem *items;
@@ -503,13 +539,13 @@ pg_wait_sampling_get_current(PG_FUNCTION_ARGS)
503539
{
504540
PGPROC *proc = &ProcGlobal->allProcs[i];
505541

506-
if (proc != NULL && proc->pid != 0 && proc->wait_event_info)
507-
{
508-
params->items[j].pid = proc->pid;
509-
params->items[j].wait_event_info = proc->wait_event_info;
510-
params->items[j].queryId = pgws_proc_queryids[i];
511-
j++;
512-
}
542+
if (!pgws_should_sample_proc(proc))
543+
continue;
544+
545+
params->items[j].pid = proc->pid;
546+
params->items[j].wait_event_info = proc->wait_event_info;
547+
params->items[j].queryId = pgws_proc_queryids[i];
548+
j++;
513549
}
514550
funcctx->max_calls = j;
515551
}

pg_wait_sampling.h

+2
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,15 @@ typedef struct
6363
int profilePeriod;
6464
bool profilePid;
6565
bool profileQueries;
66+
bool sampleCpu;
6667
} CollectorShmqHeader;
6768

6869
/* pg_wait_sampling.c */
6970
extern CollectorShmqHeader *pgws_collector_hdr;
7071
extern shm_mq *pgws_collector_mq;
7172
extern uint64 *pgws_proc_queryids;
7273
extern void pgws_init_lock_tag(LOCKTAG *tag, uint32 lock);
74+
extern bool pgws_should_sample_proc(PGPROC *proc);
7375

7476
/* collector.c */
7577
extern void pgws_register_wait_collector(void);

0 commit comments

Comments
 (0)