Skip to content

Commit 7cc9d5b

Browse files
author
Oleg Tselebrovskiy
committed
Add profile_extended and history_extended views with additional dimensions
Sometimes it can be useful to have additional info collected with wait_events, so we add two new views/functions that include more information. The structure of those views could be changed in new versions of pg_wait_sampling extension
1 parent 3c1046c commit 7cc9d5b

9 files changed

+1067
-23
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ MODULE_big = pg_wait_sampling
44
OBJS = pg_wait_sampling.o collector.o
55

66
EXTENSION = pg_wait_sampling
7-
DATA = pg_wait_sampling--1.1.sql pg_wait_sampling--1.0--1.1.sql
7+
DATA = pg_wait_sampling--1.1.sql pg_wait_sampling--1.0--1.1.sql pg_wait_sampling--1.1--1.2.sql
88

99
REGRESS = load queries
1010

collector.c

Lines changed: 106 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "postgres.h"
1111

1212
#include <signal.h>
13+
#include <time.h>
1314

1415
#include "compat.h"
1516
#include "miscadmin.h"
@@ -30,6 +31,13 @@
3031
#include "utils/resowner.h"
3132
#include "utils/timestamp.h"
3233

34+
#define check_bestatus_dimensions(dimensions) \
35+
(dimensions & (PGWS_DIMENSIONS_BE_TYPE |\
36+
PGWS_DIMENSIONS_BE_STATE |\
37+
PGWS_DIMENSIONS_BE_START_TIME |\
38+
PGWS_DIMENSIONS_CLIENT_ADDR |\
39+
PGWS_DIMENSIONS_CLIENT_HOSTNAME |\
40+
PGWS_DIMENSIONS_APPNAME))
3341
static volatile sig_atomic_t shutdown_requested = false;
3442

3543
static void handle_sigterm(SIGNAL_ARGS);
@@ -162,25 +170,103 @@ probe_waits(History *observations, HTAB *profile_hash,
162170
LWLockAcquire(ProcArrayLock, LW_SHARED);
163171
for (i = 0; i < ProcGlobal->allProcCount; i++)
164172
{
165-
HistoryItem item,
173+
HistoryItem item_history,
166174
*observation;
175+
ProfileItem item_profile;
167176
PGPROC *proc = &ProcGlobal->allProcs[i];
177+
int pid;
178+
uint32 wait_event_info;
168179

169-
if (!pgws_should_sample_proc(proc, &item.pid, &item.wait_event_info))
180+
/* Check if we need to sample this process */
181+
if (!pgws_should_sample_proc(proc, &pid, &wait_event_info))
170182
continue;
171183

184+
/* We zero whole HistoryItem to avoid doing it field-by-field */
185+
memset(&item_history, 0, sizeof(HistoryItem));
186+
memset(&item_profile, 0, sizeof(ProfileItem));
187+
188+
item_history.pid = pid;
189+
item_profile.pid = pid;
190+
191+
item_history.wait_event_info = wait_event_info;
192+
item_profile.wait_event_info = wait_event_info;
193+
172194
if (pgws_profileQueries)
173-
item.queryId = pgws_proc_queryids[i];
174-
else
175-
item.queryId = 0;
195+
{
196+
item_history.queryId = pgws_proc_queryids[i];
197+
item_profile.queryId = pgws_proc_queryids[i];
198+
}
176199

177-
item.ts = ts;
200+
item_history.ts = ts;
201+
202+
/* Copy everything we need from PGPROC */
203+
if (pgws_history_dimensions & PGWS_DIMENSIONS_ROLE_ID)
204+
item_history.role_id = proc->roleId;
205+
if (pgws_profile_dimensions & PGWS_DIMENSIONS_ROLE_ID)
206+
item_profile.role_id = proc->roleId;
207+
208+
if (pgws_history_dimensions & PGWS_DIMENSIONS_DB_ID)
209+
item_history.database_id = proc->databaseId;
210+
if (pgws_profile_dimensions & PGWS_DIMENSIONS_DB_ID)
211+
item_profile.database_id = proc->databaseId;
212+
213+
if (pgws_history_dimensions & PGWS_DIMENSIONS_PARALLEL_LEADER_PID)
214+
item_history.parallel_leader_pid = (proc->lockGroupLeader ?
215+
proc->lockGroupLeader->pid :
216+
0);
217+
if (pgws_profile_dimensions & PGWS_DIMENSIONS_PARALLEL_LEADER_PID)
218+
item_profile.parallel_leader_pid = (proc->lockGroupLeader ?
219+
proc->lockGroupLeader->pid :
220+
0);
221+
/* Look into BackendStatus only if necessary */
222+
if (check_bestatus_dimensions(pgws_history_dimensions) ||
223+
check_bestatus_dimensions(pgws_profile_dimensions))
224+
{
225+
#if PG_VERSION_NUM >= 170000
226+
PgBackendStatus *bestatus = pgstat_get_beentry_by_proc_number(GetNumberFromPGProc(proc));
227+
#else
228+
PgBackendStatus *bestatus = get_beentry_by_procpid(proc->pid);
229+
#endif
230+
/* Copy everything we need from BackendStatus */
231+
if (bestatus)
232+
{
233+
if (pgws_history_dimensions & PGWS_DIMENSIONS_BE_TYPE)
234+
item_history.backend_type = bestatus->st_backendType;
235+
if (pgws_profile_dimensions & PGWS_DIMENSIONS_BE_TYPE)
236+
item_profile.backend_type = bestatus->st_backendType;
237+
238+
if (pgws_history_dimensions & PGWS_DIMENSIONS_BE_STATE)
239+
item_history.backend_state = bestatus->st_state;
240+
if (pgws_profile_dimensions & PGWS_DIMENSIONS_BE_STATE)
241+
item_profile.backend_state = bestatus->st_state;
242+
243+
if (pgws_history_dimensions & PGWS_DIMENSIONS_BE_START_TIME)
244+
item_history.proc_start = bestatus->st_proc_start_timestamp;
245+
if (pgws_profile_dimensions & PGWS_DIMENSIONS_BE_START_TIME)
246+
item_profile.proc_start = bestatus->st_proc_start_timestamp;
247+
248+
if (pgws_history_dimensions & PGWS_DIMENSIONS_CLIENT_ADDR)
249+
item_history.client_addr = bestatus->st_clientaddr;
250+
if (pgws_profile_dimensions & PGWS_DIMENSIONS_CLIENT_ADDR)
251+
item_profile.client_addr = bestatus->st_clientaddr;
252+
253+
if (pgws_history_dimensions & PGWS_DIMENSIONS_CLIENT_HOSTNAME)
254+
strcpy(item_history.client_hostname, bestatus->st_clienthostname);
255+
if (pgws_profile_dimensions & PGWS_DIMENSIONS_CLIENT_HOSTNAME)
256+
strcpy(item_profile.client_hostname, bestatus->st_clienthostname);
257+
258+
if (pgws_history_dimensions & PGWS_DIMENSIONS_APPNAME)
259+
strcpy(item_history.appname, bestatus->st_appname);
260+
if (pgws_profile_dimensions & PGWS_DIMENSIONS_APPNAME)
261+
strcpy(item_profile.appname, bestatus->st_appname);
262+
}
263+
}
178264

179265
/* Write to the history if needed */
180266
if (write_history)
181267
{
182268
observation = get_next_observation(observations);
183-
*observation = item;
269+
*observation = item_history;
184270
}
185271

186272
/* Write to the profile if needed */
@@ -190,16 +276,21 @@ probe_waits(History *observations, HTAB *profile_hash,
190276
bool found;
191277

192278
if (!profile_pid)
193-
item.pid = 0;
279+
item_profile.pid = 0;
194280

195-
profileItem = (ProfileItem *) hash_search(profile_hash, &item, HASH_ENTER, &found);
281+
profileItem = (ProfileItem *) hash_search(profile_hash, &item_profile, HASH_ENTER, &found);
196282
if (found)
197283
profileItem->count++;
198284
else
199285
profileItem->count = 1;
200286
}
201287
}
202288
LWLockRelease(ProcArrayLock);
289+
#if PG_VERSION_NUM >= 140000
290+
pgstat_clear_backend_activity_snapshot();
291+
#else
292+
pgstat_clear_snapshot();
293+
#endif
203294
}
204295

205296
/*
@@ -287,10 +378,12 @@ make_profile_hash()
287378
{
288379
HASHCTL hash_ctl;
289380

290-
if (pgws_profileQueries)
291-
hash_ctl.keysize = offsetof(ProfileItem, count);
292-
else
293-
hash_ctl.keysize = offsetof(ProfileItem, queryId);
381+
/*
382+
* Since adding additional dimensions we include everyting except count
383+
* into hashtable key. This is fine for cases when some fields are 0 since
384+
* it doesn't impede our ability to search the hash table for entries
385+
*/
386+
hash_ctl.keysize = offsetof(ProfileItem, count);
294387

295388
hash_ctl.entrysize = sizeof(ProfileItem);
296389
return hash_create("Waits profile hash", 1024, &hash_ctl,

expected/queries.out

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,27 @@ WITH t as (SELECT sum(0) FROM pg_wait_sampling_profile)
2020
0
2121
(1 row)
2222

23+
WITH t as (SELECT sum(0) FROM pg_wait_sampling_current_extended)
24+
SELECT sum(0) FROM generate_series(1, 2), t;
25+
sum
26+
-----
27+
0
28+
(1 row)
29+
30+
WITH t as (SELECT sum(0) FROM pg_wait_sampling_history_extended)
31+
SELECT sum(0) FROM generate_series(1, 2), t;
32+
sum
33+
-----
34+
0
35+
(1 row)
36+
37+
WITH t as (SELECT sum(0) FROM pg_wait_sampling_profile_extended)
38+
SELECT sum(0) FROM generate_series(1, 2), t;
39+
sum
40+
-----
41+
0
42+
(1 row)
43+
2344
-- Some dummy checks just to be sure that all our functions work and return something.
2445
SELECT count(*) = 1 as test FROM pg_wait_sampling_get_current(pg_backend_pid());
2546
test
@@ -45,4 +66,28 @@ SELECT pg_wait_sampling_reset_profile();
4566

4667
(1 row)
4768

69+
SELECT count(*) = 1 as test FROM pg_wait_sampling_get_current_extended(pg_backend_pid());
70+
test
71+
------
72+
t
73+
(1 row)
74+
75+
SELECT count(*) >= 0 as test FROM pg_wait_sampling_get_profile_extended();
76+
test
77+
------
78+
t
79+
(1 row)
80+
81+
SELECT count(*) >= 0 as test FROM pg_wait_sampling_get_history_extended();
82+
test
83+
------
84+
t
85+
(1 row)
86+
87+
SELECT pg_wait_sampling_reset_profile();
88+
pg_wait_sampling_reset_profile
89+
--------------------------------
90+
91+
(1 row)
92+
4893
DROP EXTENSION pg_wait_sampling;

meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ install_data(
2424
'pg_wait_sampling.control',
2525
'pg_wait_sampling--1.0--1.1.sql',
2626
'pg_wait_sampling--1.1.sql',
27+
'pg_wait_sampling--1.1--1.2.sql',
2728
kwargs: contrib_data_args,
2829
)
2930

pg_wait_sampling--1.1--1.2.sql

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/* contrib/pg_wait_sampling/pg_wait_sampling--1.1--1.2.sql */
2+
3+
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
4+
\echo Use "ALTER EXTENSION pg_wait_sampling UPDATE TO 1.2" to load this file. \quit
5+
6+
CREATE FUNCTION pg_wait_sampling_get_current_extended (
7+
pid int4,
8+
OUT pid int4,
9+
OUT event_type text,
10+
OUT event text,
11+
OUT queryid int8,
12+
OUT role_id int8,
13+
OUT database_id int8,
14+
OUT parallel_leader_pid int4,
15+
OUT backend_type text,
16+
OUT backend_state text,
17+
OUT proc_start timestamptz,
18+
OUT client_addr text,
19+
OUT client_hostname text,
20+
OUT appname text
21+
)
22+
RETURNS SETOF record
23+
AS 'MODULE_PATHNAME'
24+
LANGUAGE C VOLATILE CALLED ON NULL INPUT;
25+
26+
CREATE VIEW pg_wait_sampling_current_extended AS
27+
SELECT * FROM pg_wait_sampling_get_current_extended(NULL::integer);
28+
29+
GRANT SELECT ON pg_wait_sampling_current TO PUBLIC;
30+
31+
CREATE FUNCTION pg_wait_sampling_get_history_extended (
32+
OUT pid int4,
33+
OUT ts timestamptz,
34+
OUT event_type text,
35+
OUT event text,
36+
OUT queryid int8,
37+
OUT role_id int8,
38+
OUT database_id int8,
39+
OUT parallel_leader_pid int4,
40+
OUT backend_type text,
41+
OUT backend_state text,
42+
OUT proc_start timestamptz,
43+
OUT client_addr text,
44+
OUT client_hostname text,
45+
OUT appname text
46+
)
47+
RETURNS SETOF record
48+
AS 'MODULE_PATHNAME'
49+
LANGUAGE C VOLATILE STRICT;
50+
51+
CREATE VIEW pg_wait_sampling_history_extended AS
52+
SELECT * FROM pg_wait_sampling_get_history_extended();
53+
54+
GRANT SELECT ON pg_wait_sampling_history_extended TO PUBLIC;
55+
56+
CREATE FUNCTION pg_wait_sampling_get_profile_extended (
57+
OUT pid int4,
58+
OUT event_type text,
59+
OUT event text,
60+
OUT queryid int8,
61+
OUT role_id int8,
62+
OUT database_id int8,
63+
OUT parallel_leader_pid int4,
64+
OUT backend_type text,
65+
OUT backend_state text,
66+
OUT proc_start timestamptz,
67+
OUT client_addr text,
68+
OUT client_hostname text,
69+
OUT appname text,
70+
OUT count int8
71+
)
72+
RETURNS SETOF record
73+
AS 'MODULE_PATHNAME'
74+
LANGUAGE C VOLATILE STRICT;
75+
76+
CREATE VIEW pg_wait_sampling_profile_extended AS
77+
SELECT * FROM pg_wait_sampling_get_profile_extended();
78+
79+
GRANT SELECT ON pg_wait_sampling_profile_extended TO PUBLIC;

0 commit comments

Comments
 (0)