Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 0b360d6

Browse files
Ilia EvdokimovCommitfest Bot
Ilia Evdokimov
authored and
Commitfest Bot
committed
Allow setting sample rate for pg_stat_statements
New configuration parameter pg_stat_statements.sample_rate makes it possible to track just a fraction of the queries meeting the configured threshold, to reduce the amount of tracking.
1 parent 82a46cc commit 0b360d6

File tree

6 files changed

+291
-8
lines changed

6 files changed

+291
-8
lines changed

contrib/pg_stat_statements/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ LDFLAGS_SL += $(filter -lm, $(LIBS))
2020
REGRESS_OPTS = --temp-config $(top_srcdir)/contrib/pg_stat_statements/pg_stat_statements.conf
2121
REGRESS = select dml cursors utility level_tracking planning \
2222
user_activity wal entry_timestamp privileges extended \
23-
parallel cleanup oldextversions squashing
23+
parallel sampling cleanup oldextversions squashing
2424
# Disabled because these tests require "shared_preload_libraries=pg_stat_statements",
2525
# which typical installcheck users do not have (e.g. buildfarm clients).
2626
NO_INSTALLCHECK = 1
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
--
2+
-- sample statements
3+
--
4+
-- top-level tracking - simple query protocol
5+
SHOW pg_stat_statements.track;
6+
pg_stat_statements.track
7+
--------------------------
8+
top
9+
(1 row)
10+
11+
SET pg_stat_statements.sample_rate = 0.0;
12+
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
13+
t
14+
---
15+
t
16+
(1 row)
17+
18+
SELECT 1 AS "int";
19+
int
20+
-----
21+
1
22+
(1 row)
23+
24+
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
25+
query | calls
26+
-------+-------
27+
(0 rows)
28+
29+
SET pg_stat_statements.sample_rate = 1.0;
30+
SELECT 1 AS "int";
31+
int
32+
-----
33+
1
34+
(1 row)
35+
36+
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
37+
query | calls
38+
--------------------+-------
39+
SELECT $1 AS "int" | 1
40+
(1 row)
41+
42+
-- top-level tracking - extended query protocol
43+
SET pg_stat_statements.sample_rate = 0.0;
44+
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
45+
t
46+
---
47+
t
48+
(1 row)
49+
50+
SELECT 1 \parse stmt
51+
\bind_named stmt \g
52+
?column?
53+
----------
54+
1
55+
(1 row)
56+
57+
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
58+
query | calls
59+
-------+-------
60+
(0 rows)
61+
62+
SET pg_stat_statements.sample_rate = 1.0;
63+
\bind_named stmt \g
64+
?column?
65+
----------
66+
1
67+
(1 row)
68+
69+
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
70+
query | calls
71+
-----------+-------
72+
SELECT $1 | 1
73+
(1 row)
74+
75+
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
76+
t
77+
---
78+
t
79+
(1 row)
80+
81+
DEALLOCATE stmt;
82+
-- nested tracking - simple query protocol
83+
SET pg_stat_statements.track = "all";
84+
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
85+
t
86+
---
87+
t
88+
(1 row)
89+
90+
SET pg_stat_statements.sample_rate = 1;
91+
EXPLAIN (COSTS OFF) SELECT 1;
92+
QUERY PLAN
93+
------------
94+
Result
95+
(1 row)
96+
97+
EXPLAIN (COSTS OFF) SELECT 1;
98+
QUERY PLAN
99+
------------
100+
Result
101+
(1 row)
102+
103+
SET pg_stat_statements.sample_rate = 0;
104+
EXPLAIN (COSTS OFF) SELECT 1;
105+
QUERY PLAN
106+
------------
107+
Result
108+
(1 row)
109+
110+
EXPLAIN (COSTS OFF) SELECT 1;
111+
QUERY PLAN
112+
------------
113+
Result
114+
(1 row)
115+
116+
SELECT toplevel, calls, query FROM pg_stat_statements
117+
ORDER BY query COLLATE "C";
118+
toplevel | calls | query
119+
----------+-------+----------------------------------------------------
120+
t | 2 | EXPLAIN (COSTS OFF) SELECT $1
121+
f | 2 | SELECT $1
122+
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
123+
t | 2 | SET pg_stat_statements.sample_rate = $1
124+
(4 rows)
125+
126+
-- nested tracking - extended query protocol
127+
SET pg_stat_statements.track = "all";
128+
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
129+
t
130+
---
131+
t
132+
(1 row)
133+
134+
SET pg_stat_statements.sample_rate = 1;
135+
EXPLAIN (COSTS OFF) SELECT 1; \parse stmt
136+
QUERY PLAN
137+
------------
138+
Result
139+
(1 row)
140+
141+
\bind_named stmt \g
142+
QUERY PLAN
143+
------------
144+
Result
145+
(1 row)
146+
147+
\bind_named stmt \g
148+
QUERY PLAN
149+
------------
150+
Result
151+
(1 row)
152+
153+
SET pg_stat_statements.sample_rate = 0;
154+
\bind_named stmt \g
155+
QUERY PLAN
156+
------------
157+
Result
158+
(1 row)
159+
160+
\bind_named stmt \g
161+
QUERY PLAN
162+
------------
163+
Result
164+
(1 row)
165+
166+
SELECT toplevel, calls, query FROM pg_stat_statements
167+
ORDER BY query COLLATE "C";
168+
toplevel | calls | query
169+
----------+-------+-----------------------------------------
170+
t | 2 | EXPLAIN (COSTS OFF) SELECT $1
171+
f | 3 | SELECT $1
172+
t | 1 | SET pg_stat_statements.sample_rate = $1
173+
(3 rows)
174+

contrib/pg_stat_statements/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ tests += {
5454
'privileges',
5555
'extended',
5656
'parallel',
57+
'sampling',
5758
'cleanup',
5859
'oldextversions',
5960
'squashing',

contrib/pg_stat_statements/pg_stat_statements.c

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include "access/parallel.h"
5151
#include "catalog/pg_authid.h"
5252
#include "common/int.h"
53+
#include "common/pg_prng.h"
5354
#include "executor/instrument.h"
5455
#include "funcapi.h"
5556
#include "jit/jit.h"
@@ -260,6 +261,9 @@ typedef struct pgssSharedState
260261
/* Current nesting depth of planner/ExecutorRun/ProcessUtility calls */
261262
static int nesting_level = 0;
262263

264+
/* Is the current top-level query to be sampled? */
265+
static bool is_query_sampled = false;
266+
263267
/* Saved hook values */
264268
static shmem_request_hook_type prev_shmem_request_hook = NULL;
265269
static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
@@ -298,11 +302,13 @@ static bool pgss_track_utility = true; /* whether to track utility commands */
298302
static bool pgss_track_planning = false; /* whether to track planning
299303
* duration */
300304
static bool pgss_save = true; /* whether to save stats across shutdown */
305+
static double pgss_sample_rate = 1.0; /* fraction of statements to track */
301306

302-
#define pgss_enabled(level) \
307+
#define pgss_enabled(level, skip_sampling_check) \
303308
(!IsParallelWorker() && \
304309
(pgss_track == PGSS_TRACK_ALL || \
305-
(pgss_track == PGSS_TRACK_TOP && (level) == 0)))
310+
(pgss_track == PGSS_TRACK_TOP && (level) == 0)) && \
311+
(skip_sampling_check == PGSS_INVALID || current_query_sampled()))
306312

307313
#define record_gc_qtexts() \
308314
do { \
@@ -376,6 +382,7 @@ static char *generate_normalized_query(JumbleState *jstate, const char *query,
376382
static void fill_in_constant_lengths(JumbleState *jstate, const char *query,
377383
int query_loc);
378384
static int comp_location(const void *a, const void *b);
385+
static bool current_query_sampled(void);
379386

380387

381388
/*
@@ -417,6 +424,19 @@ _PG_init(void)
417424
NULL,
418425
NULL);
419426

427+
DefineCustomRealVariable("pg_stat_statements.sample_rate",
428+
"Fraction of queries to track.",
429+
NULL,
430+
&pgss_sample_rate,
431+
1.0,
432+
0.0,
433+
1.0,
434+
PGC_SUSET,
435+
0,
436+
NULL,
437+
NULL,
438+
NULL);
439+
420440
DefineCustomEnumVariable("pg_stat_statements.track",
421441
"Selects which statements are tracked by pg_stat_statements.",
422442
NULL,
@@ -839,7 +859,7 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
839859
prev_post_parse_analyze_hook(pstate, query, jstate);
840860

841861
/* Safety check... */
842-
if (!pgss || !pgss_hash || !pgss_enabled(nesting_level))
862+
if (!pgss || !pgss_hash || !pgss_enabled(nesting_level, PGSS_INVALID))
843863
return;
844864

845865
/*
@@ -897,7 +917,7 @@ pgss_planner(Query *parse,
897917
* pgss_store needs it. We also ignore query without queryid, as it would
898918
* be treated as a utility statement, which may not be the case.
899919
*/
900-
if (pgss_enabled(nesting_level)
920+
if (pgss_enabled(nesting_level, PGSS_PLAN)
901921
&& pgss_track_planning && query_string
902922
&& parse->queryId != UINT64CONST(0))
903923
{
@@ -1008,7 +1028,8 @@ pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
10081028
* counting of optimizable statements that are directly contained in
10091029
* utility statements.
10101030
*/
1011-
if (pgss_enabled(nesting_level) && queryDesc->plannedstmt->queryId != UINT64CONST(0))
1031+
if (pgss_enabled(nesting_level, PGSS_EXEC) &&
1032+
queryDesc->plannedstmt->queryId != UINT64CONST(0))
10121033
{
10131034
/*
10141035
* Set up to track total elapsed time in ExecutorRun. Make sure the
@@ -1079,7 +1100,7 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
10791100
uint64 queryId = queryDesc->plannedstmt->queryId;
10801101

10811102
if (queryId != UINT64CONST(0) && queryDesc->totaltime &&
1082-
pgss_enabled(nesting_level))
1103+
pgss_enabled(nesting_level, PGSS_EXEC))
10831104
{
10841105
/*
10851106
* Make sure stats accumulation is done. (Note: it's okay if several
@@ -1122,7 +1143,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
11221143
uint64 saved_queryId = pstmt->queryId;
11231144
int saved_stmt_location = pstmt->stmt_location;
11241145
int saved_stmt_len = pstmt->stmt_len;
1125-
bool enabled = pgss_track_utility && pgss_enabled(nesting_level);
1146+
bool enabled = pgss_track_utility && pgss_enabled(nesting_level, PGSS_EXEC);
11261147

11271148
/*
11281149
* Force utility statements to get queryId zero. We do this even in cases
@@ -3084,3 +3105,21 @@ comp_location(const void *a, const void *b)
30843105

30853106
return pg_cmp_s32(l, r);
30863107
}
3108+
3109+
/*
3110+
* Determine whether the current query should be sampled.
3111+
*
3112+
* At the beginning of each top-level statement, decide whether we'll
3113+
* sample this statement. If nested-statement tracking is enabled,
3114+
* either all nested statements will be tracked or none will.
3115+
*/
3116+
static bool
3117+
current_query_sampled(void)
3118+
{
3119+
if (nesting_level == 0)
3120+
is_query_sampled = pgss_sample_rate != 0.0 &&
3121+
(pgss_sample_rate == 1.0 ||
3122+
pg_prng_double(&pg_global_prng_state) <= pgss_sample_rate);
3123+
3124+
return is_query_sampled;
3125+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
--
2+
-- sample statements
3+
--
4+
5+
-- top-level tracking - simple query protocol
6+
SHOW pg_stat_statements.track;
7+
SET pg_stat_statements.sample_rate = 0.0;
8+
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
9+
SELECT 1 AS "int";
10+
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
11+
SET pg_stat_statements.sample_rate = 1.0;
12+
SELECT 1 AS "int";
13+
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
14+
15+
-- top-level tracking - extended query protocol
16+
SET pg_stat_statements.sample_rate = 0.0;
17+
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
18+
SELECT 1 \parse stmt
19+
\bind_named stmt \g
20+
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
21+
SET pg_stat_statements.sample_rate = 1.0;
22+
\bind_named stmt \g
23+
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
24+
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
25+
DEALLOCATE stmt;
26+
27+
-- nested tracking - simple query protocol
28+
SET pg_stat_statements.track = "all";
29+
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
30+
SET pg_stat_statements.sample_rate = 1;
31+
EXPLAIN (COSTS OFF) SELECT 1;
32+
EXPLAIN (COSTS OFF) SELECT 1;
33+
SET pg_stat_statements.sample_rate = 0;
34+
EXPLAIN (COSTS OFF) SELECT 1;
35+
EXPLAIN (COSTS OFF) SELECT 1;
36+
SELECT toplevel, calls, query FROM pg_stat_statements
37+
ORDER BY query COLLATE "C";
38+
39+
-- nested tracking - extended query protocol
40+
SET pg_stat_statements.track = "all";
41+
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
42+
SET pg_stat_statements.sample_rate = 1;
43+
EXPLAIN (COSTS OFF) SELECT 1; \parse stmt
44+
\bind_named stmt \g
45+
\bind_named stmt \g
46+
SET pg_stat_statements.sample_rate = 0;
47+
\bind_named stmt \g
48+
\bind_named stmt \g
49+
SELECT toplevel, calls, query FROM pg_stat_statements
50+
ORDER BY query COLLATE "C";

doc/src/sgml/pgstatstatements.sgml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,6 +966,25 @@ calls | 2
966966
</listitem>
967967
</varlistentry>
968968

969+
<varlistentry>
970+
<term>
971+
<varname>pg_stat_statements.sample_rate</varname> (<type>real</type>)
972+
<indexterm>
973+
<primary><varname>pg_stat_statements.sample_rate</varname> configuration parameter</primary>
974+
</indexterm>
975+
</term>
976+
977+
<listitem>
978+
<para>
979+
<varname>pg_stat_statements.sample_rate</varname> causes pg_stat_statements to only
980+
track a fraction of the statements in each session. The default is <literal>1</literal>,
981+
meaning track all the queries. Setting this to <literal>0</literal> disables sampled statements
982+
tracking, the same as setting <varname>pg_stat_statements.track</varname> to <literal>none</literal>.
983+
In case of nested statements, either all will be tracked or none. Only superusers can change this setting.
984+
</para>
985+
</listitem>
986+
</varlistentry>
987+
969988
<varlistentry>
970989
<term>
971990
<varname>pg_stat_statements.save</varname> (<type>boolean</type>)

0 commit comments

Comments
 (0)