Skip to content

Commit 6b466bf

Browse files
author
Amit Kapila
committed
Allow pg_stat_statements to track WAL usage statistics.
This commit adds three new columns in pg_stat_statements output to display WAL usage statistics added by commit df3b181. This commit doesn't bump the version of pg_stat_statements as the same is done for this release in commit 17e0328. Author: Kirill Bychik and Julien Rouhaud Reviewed-by: Julien Rouhaud, Fujii Masao, Dilip Kumar and Amit Kapila Discussion: https://postgr.es/m/CAB-hujrP8ZfUkvL5OYETipQwA=e3n7oqHFU=4ZLxWS_Cza3kQQ@mail.gmail.com
1 parent 70de4e9 commit 6b466bf

File tree

5 files changed

+145
-4
lines changed

5 files changed

+145
-4
lines changed

contrib/pg_stat_statements/expected/pg_stat_statements.out

+39
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,45 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
211211
UPDATE test SET b = $1 WHERE a > $2 | 1 | 3
212212
(10 rows)
213213

214+
--
215+
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
216+
--
217+
SELECT pg_stat_statements_reset();
218+
pg_stat_statements_reset
219+
--------------------------
220+
221+
(1 row)
222+
223+
-- utility "create table" should not be shown
224+
CREATE TABLE pgss_test (a int, b char(20));
225+
INSERT INTO pgss_test VALUES(generate_series(1, 10), 'aaa');
226+
UPDATE pgss_test SET b = 'bbb' WHERE a > 7;
227+
DELETE FROM pgss_test WHERE a > 9;
228+
-- DROP test table
229+
SET pg_stat_statements.track_utility = TRUE;
230+
DROP TABLE pgss_test;
231+
SET pg_stat_statements.track_utility = FALSE;
232+
-- Check WAL is generated for the above statements
233+
SELECT query, calls, rows,
234+
wal_bytes > 0 as wal_bytes_generated,
235+
wal_records > 0 as wal_records_generated,
236+
wal_records = rows as wal_records_as_rows
237+
FROM pg_stat_statements ORDER BY query COLLATE "C";
238+
query | calls | rows | wal_bytes_generated | wal_records_generated | wal_records_as_rows
239+
-----------------------------------------------------------+-------+------+---------------------+-----------------------+---------------------
240+
DELETE FROM pgss_test WHERE a > $1 | 1 | 1 | t | t | t
241+
DROP TABLE pgss_test | 1 | 0 | t | t | f
242+
INSERT INTO pgss_test VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
243+
SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
244+
SELECT query, calls, rows, +| 0 | 0 | f | f | t
245+
wal_bytes > $1 as wal_bytes_generated, +| | | | |
246+
wal_records > $2 as wal_records_generated, +| | | | |
247+
wal_records = rows as wal_records_as_rows +| | | | |
248+
FROM pg_stat_statements ORDER BY query COLLATE "C" | | | | |
249+
SET pg_stat_statements.track_utility = FALSE | 1 | 0 | f | f | t
250+
UPDATE pgss_test SET b = $1 WHERE a > $2 | 1 | 3 | t | t | t
251+
(7 rows)
252+
214253
--
215254
-- pg_stat_statements.track = none
216255
--

contrib/pg_stat_statements/pg_stat_statements--1.7--1.8.sql

+4-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ CREATE FUNCTION pg_stat_statements(IN showtext boolean,
4141
OUT temp_blks_read int8,
4242
OUT temp_blks_written int8,
4343
OUT blk_read_time float8,
44-
OUT blk_write_time float8
44+
OUT blk_write_time float8,
45+
OUT wal_records int8,
46+
OUT wal_num_fpw int8,
47+
OUT wal_bytes numeric
4548
)
4649
RETURNS SETOF record
4750
AS 'MODULE_PATHNAME', 'pg_stat_statements_1_8'

contrib/pg_stat_statements/pg_stat_statements.c

+52-3
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ typedef struct Counters
188188
double blk_read_time; /* time spent reading, in msec */
189189
double blk_write_time; /* time spent writing, in msec */
190190
double usage; /* usage factor */
191+
int64 wal_records; /* # of WAL records generated */
192+
int64 wal_num_fpw; /* # of WAL full page image records generated */
193+
uint64 wal_bytes; /* total amount of WAL bytes generated */
191194
} Counters;
192195

193196
/*
@@ -348,6 +351,7 @@ static void pgss_store(const char *query, uint64 queryId,
348351
pgssStoreKind kind,
349352
double total_time, uint64 rows,
350353
const BufferUsage *bufusage,
354+
const WalUsage *walusage,
351355
pgssJumbleState *jstate);
352356
static void pg_stat_statements_internal(FunctionCallInfo fcinfo,
353357
pgssVersion api_version,
@@ -891,6 +895,7 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query)
891895
0,
892896
0,
893897
NULL,
898+
NULL,
894899
&jstate);
895900
}
896901

@@ -926,9 +931,17 @@ pgss_planner(Query *parse,
926931
instr_time duration;
927932
BufferUsage bufusage_start,
928933
bufusage;
934+
WalUsage walusage_start,
935+
walusage;
929936

930937
/* We need to track buffer usage as the planner can access them. */
931938
bufusage_start = pgBufferUsage;
939+
940+
/*
941+
* Similarly the planner could write some WAL records in some cases
942+
* (e.g. setting a hint bit with those being WAL-logged)
943+
*/
944+
walusage_start = pgWalUsage;
932945
INSTR_TIME_SET_CURRENT(start);
933946

934947
plan_nested_level++;
@@ -954,6 +967,10 @@ pgss_planner(Query *parse,
954967
memset(&bufusage, 0, sizeof(BufferUsage));
955968
BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
956969

970+
/* calc differences of WAL counters. */
971+
memset(&walusage, 0, sizeof(WalUsage));
972+
WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
973+
957974
pgss_store(query_string,
958975
parse->queryId,
959976
parse->stmt_location,
@@ -962,6 +979,7 @@ pgss_planner(Query *parse,
962979
INSTR_TIME_GET_MILLISEC(duration),
963980
0,
964981
&bufusage,
982+
&walusage,
965983
NULL);
966984
}
967985
else
@@ -1079,6 +1097,7 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
10791097
queryDesc->totaltime->total * 1000.0, /* convert to msec */
10801098
queryDesc->estate->es_processed,
10811099
&queryDesc->totaltime->bufusage,
1100+
&queryDesc->totaltime->walusage,
10821101
NULL);
10831102
}
10841103

@@ -1123,8 +1142,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
11231142
uint64 rows;
11241143
BufferUsage bufusage_start,
11251144
bufusage;
1145+
WalUsage walusage_start,
1146+
walusage;
11261147

11271148
bufusage_start = pgBufferUsage;
1149+
walusage_start = pgWalUsage;
11281150
INSTR_TIME_SET_CURRENT(start);
11291151

11301152
exec_nested_level++;
@@ -1154,6 +1176,10 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
11541176
memset(&bufusage, 0, sizeof(BufferUsage));
11551177
BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
11561178

1179+
/* calc differences of WAL counters. */
1180+
memset(&walusage, 0, sizeof(WalUsage));
1181+
WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
1182+
11571183
pgss_store(queryString,
11581184
0, /* signal that it's a utility stmt */
11591185
pstmt->stmt_location,
@@ -1162,6 +1188,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
11621188
INSTR_TIME_GET_MILLISEC(duration),
11631189
rows,
11641190
&bufusage,
1191+
&walusage,
11651192
NULL);
11661193
}
11671194
else
@@ -1197,7 +1224,8 @@ pgss_hash_string(const char *str, int len)
11971224
*
11981225
* If jstate is not NULL then we're trying to create an entry for which
11991226
* we have no statistics as yet; we just want to record the normalized
1200-
* query string. total_time, rows, bufusage are ignored in this case.
1227+
* query string. total_time, rows, bufusage and walusage are ignored in this
1228+
* case.
12011229
*
12021230
* If kind is PGSS_PLAN or PGSS_EXEC, its value is used as the array position
12031231
* for the arrays in the Counters field.
@@ -1208,6 +1236,7 @@ pgss_store(const char *query, uint64 queryId,
12081236
pgssStoreKind kind,
12091237
double total_time, uint64 rows,
12101238
const BufferUsage *bufusage,
1239+
const WalUsage *walusage,
12111240
pgssJumbleState *jstate)
12121241
{
12131242
pgssHashKey key;
@@ -1402,6 +1431,9 @@ pgss_store(const char *query, uint64 queryId,
14021431
e->counters.blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_read_time);
14031432
e->counters.blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_write_time);
14041433
e->counters.usage += USAGE_EXEC(total_time);
1434+
e->counters.wal_records += walusage->wal_records;
1435+
e->counters.wal_num_fpw += walusage->wal_num_fpw;
1436+
e->counters.wal_bytes += walusage->wal_bytes;
14051437

14061438
SpinLockRelease(&e->mutex);
14071439
}
@@ -1449,8 +1481,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
14491481
#define PG_STAT_STATEMENTS_COLS_V1_1 18
14501482
#define PG_STAT_STATEMENTS_COLS_V1_2 19
14511483
#define PG_STAT_STATEMENTS_COLS_V1_3 23
1452-
#define PG_STAT_STATEMENTS_COLS_V1_8 29
1453-
#define PG_STAT_STATEMENTS_COLS 29 /* maximum of above */
1484+
#define PG_STAT_STATEMENTS_COLS_V1_8 32
1485+
#define PG_STAT_STATEMENTS_COLS 32 /* maximum of above */
14541486

14551487
/*
14561488
* Retrieve statement statistics.
@@ -1786,6 +1818,23 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
17861818
values[i++] = Float8GetDatumFast(tmp.blk_read_time);
17871819
values[i++] = Float8GetDatumFast(tmp.blk_write_time);
17881820
}
1821+
if (api_version >= PGSS_V1_8)
1822+
{
1823+
char buf[256];
1824+
Datum wal_bytes;
1825+
1826+
values[i++] = Int64GetDatumFast(tmp.wal_records);
1827+
values[i++] = Int64GetDatumFast(tmp.wal_num_fpw);
1828+
1829+
snprintf(buf, sizeof buf, UINT64_FORMAT, tmp.wal_bytes);
1830+
1831+
/* Convert to numeric. */
1832+
wal_bytes = DirectFunctionCall3(numeric_in,
1833+
CStringGetDatum(buf),
1834+
ObjectIdGetDatum(0),
1835+
Int32GetDatum(-1));
1836+
values[i++] = wal_bytes;
1837+
}
17891838

17901839
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
17911840
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :

contrib/pg_stat_statements/sql/pg_stat_statements.sql

+23
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,29 @@ SELECT * FROM test WHERE a IN (1, 2, 3, 4, 5);
101101

102102
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
103103

104+
--
105+
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
106+
--
107+
SELECT pg_stat_statements_reset();
108+
109+
-- utility "create table" should not be shown
110+
CREATE TABLE pgss_test (a int, b char(20));
111+
112+
INSERT INTO pgss_test VALUES(generate_series(1, 10), 'aaa');
113+
UPDATE pgss_test SET b = 'bbb' WHERE a > 7;
114+
DELETE FROM pgss_test WHERE a > 9;
115+
-- DROP test table
116+
SET pg_stat_statements.track_utility = TRUE;
117+
DROP TABLE pgss_test;
118+
SET pg_stat_statements.track_utility = FALSE;
119+
120+
-- Check WAL is generated for the above statements
121+
SELECT query, calls, rows,
122+
wal_bytes > 0 as wal_bytes_generated,
123+
wal_records > 0 as wal_records_generated,
124+
wal_records = rows as wal_records_as_rows
125+
FROM pg_stat_statements ORDER BY query COLLATE "C";
126+
104127
--
105128
-- pg_stat_statements.track = none
106129
--

doc/src/sgml/pgstatstatements.sgml

+27
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,33 @@
264264
</entry>
265265
</row>
266266

267+
<row>
268+
<entry><structfield>wal_bytes</structfield></entry>
269+
<entry><type>numeric</type></entry>
270+
<entry></entry>
271+
<entry>
272+
Total amount of WAL bytes generated by the statement
273+
</entry>
274+
</row>
275+
276+
<row>
277+
<entry><structfield>wal_records</structfield></entry>
278+
<entry><type>bigint</type></entry>
279+
<entry></entry>
280+
<entry>
281+
Total count of WAL records generated by the statement
282+
</entry>
283+
</row>
284+
285+
<row>
286+
<entry><structfield>wal_num_fpw</structfield></entry>
287+
<entry><type>bigint</type></entry>
288+
<entry></entry>
289+
<entry>
290+
Total count of WAL full page writes generated by the statement
291+
</entry>
292+
</row>
293+
267294
</tbody>
268295
</tgroup>
269296
</table>

0 commit comments

Comments
 (0)