Skip to content

Commit 3825f65

Browse files
committed
Add max, mean, and stddev timing metrics to stat_statements collector
- Add postgres_stat_statements_max_seconds metric for maximum execution time - Add postgres_stat_statements_mean_seconds metric for average execution time - Add postgres_stat_statements_stddev_seconds metric for execution time variance - Support all PostgreSQL versions with appropriate column names - Update test cases to include new metrics Signed-off-by: Irfan Habib <[email protected]>
1 parent 94e8399 commit 3825f65

File tree

2 files changed

+72
-12
lines changed

2 files changed

+72
-12
lines changed

collector/pg_stat_statements.go

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,24 @@ var (
9494
[]string{"user", "datname", "queryid"},
9595
prometheus.Labels{},
9696
)
97+
statStatementsMaxSeconds = prometheus.NewDesc(
98+
prometheus.BuildFQName(namespace, statStatementsSubsystem, "max_seconds"),
99+
"Maximum time spent in a single execution of the statement, in seconds",
100+
[]string{"user", "datname", "queryid"},
101+
prometheus.Labels{},
102+
)
103+
statStatementsMeanSeconds = prometheus.NewDesc(
104+
prometheus.BuildFQName(namespace, statStatementsSubsystem, "mean_seconds"),
105+
"Mean time spent per execution of the statement, in seconds",
106+
[]string{"user", "datname", "queryid"},
107+
prometheus.Labels{},
108+
)
109+
statStatementsStddevSeconds = prometheus.NewDesc(
110+
prometheus.BuildFQName(namespace, statStatementsSubsystem, "stddev_seconds"),
111+
"Standard deviation of time spent in the statement, in seconds",
112+
[]string{"user", "datname", "queryid"},
113+
prometheus.Labels{},
114+
)
97115

98116
statStatementsQuery = prometheus.NewDesc(
99117
prometheus.BuildFQName(namespace, statStatementsSubsystem, "query_id"),
@@ -115,7 +133,10 @@ const (
115133
pg_stat_statements.total_time / 1000.0 as seconds_total,
116134
pg_stat_statements.rows as rows_total,
117135
pg_stat_statements.blk_read_time / 1000.0 as block_read_seconds_total,
118-
pg_stat_statements.blk_write_time / 1000.0 as block_write_seconds_total
136+
pg_stat_statements.blk_write_time / 1000.0 as block_write_seconds_total,
137+
pg_stat_statements.max_time / 1000.0 as max_seconds,
138+
pg_stat_statements.mean_time / 1000.0 as mean_seconds,
139+
pg_stat_statements.stddev_time / 1000.0 as stddev_seconds
119140
FROM pg_stat_statements
120141
JOIN pg_database
121142
ON pg_database.oid = pg_stat_statements.dbid
@@ -137,7 +158,10 @@ const (
137158
pg_stat_statements.total_exec_time / 1000.0 as seconds_total,
138159
pg_stat_statements.rows as rows_total,
139160
pg_stat_statements.blk_read_time / 1000.0 as block_read_seconds_total,
140-
pg_stat_statements.blk_write_time / 1000.0 as block_write_seconds_total
161+
pg_stat_statements.blk_write_time / 1000.0 as block_write_seconds_total,
162+
pg_stat_statements.max_exec_time / 1000.0 as max_seconds,
163+
pg_stat_statements.mean_exec_time / 1000.0 as mean_seconds,
164+
pg_stat_statements.stddev_exec_time / 1000.0 as stddev_seconds
141165
FROM pg_stat_statements
142166
JOIN pg_database
143167
ON pg_database.oid = pg_stat_statements.dbid
@@ -159,7 +183,10 @@ const (
159183
pg_stat_statements.total_exec_time / 1000.0 as seconds_total,
160184
pg_stat_statements.rows as rows_total,
161185
pg_stat_statements.shared_blk_read_time / 1000.0 as block_read_seconds_total,
162-
pg_stat_statements.shared_blk_write_time / 1000.0 as block_write_seconds_total
186+
pg_stat_statements.shared_blk_write_time / 1000.0 as block_write_seconds_total,
187+
pg_stat_statements.max_exec_time / 1000.0 as max_seconds,
188+
pg_stat_statements.mean_exec_time / 1000.0 as mean_seconds,
189+
pg_stat_statements.stddev_exec_time / 1000.0 as stddev_seconds
163190
FROM pg_stat_statements
164191
JOIN pg_database
165192
ON pg_database.oid = pg_stat_statements.dbid
@@ -201,12 +228,12 @@ func (c PGStatStatementsCollector) Update(ctx context.Context, instance *instanc
201228
for rows.Next() {
202229
var user, datname, queryid, statement sql.NullString
203230
var callsTotal, rowsTotal sql.NullInt64
204-
var secondsTotal, blockReadSecondsTotal, blockWriteSecondsTotal sql.NullFloat64
231+
var secondsTotal, blockReadSecondsTotal, blockWriteSecondsTotal, maxSeconds, meanSeconds, stddevSeconds sql.NullFloat64
205232
var columns []any
206233
if c.includeQueryStatement {
207-
columns = []any{&user, &datname, &queryid, &statement, &callsTotal, &secondsTotal, &rowsTotal, &blockReadSecondsTotal, &blockWriteSecondsTotal}
234+
columns = []any{&user, &datname, &queryid, &statement, &callsTotal, &secondsTotal, &rowsTotal, &blockReadSecondsTotal, &blockWriteSecondsTotal, &maxSeconds, &meanSeconds, &stddevSeconds}
208235
} else {
209-
columns = []any{&user, &datname, &queryid, &callsTotal, &secondsTotal, &rowsTotal, &blockReadSecondsTotal, &blockWriteSecondsTotal}
236+
columns = []any{&user, &datname, &queryid, &callsTotal, &secondsTotal, &rowsTotal, &blockReadSecondsTotal, &blockWriteSecondsTotal, &maxSeconds, &meanSeconds, &stddevSeconds}
210237
}
211238
if err := rows.Scan(columns...); err != nil {
212239
return err
@@ -280,6 +307,39 @@ func (c PGStatStatementsCollector) Update(ctx context.Context, instance *instanc
280307
userLabel, datnameLabel, queryidLabel,
281308
)
282309

310+
maxSecondsMetric := 0.0
311+
if maxSeconds.Valid {
312+
maxSecondsMetric = maxSeconds.Float64
313+
}
314+
ch <- prometheus.MustNewConstMetric(
315+
statStatementsMaxSeconds,
316+
prometheus.GaugeValue,
317+
maxSecondsMetric,
318+
userLabel, datnameLabel, queryidLabel,
319+
)
320+
321+
meanSecondsMetric := 0.0
322+
if meanSeconds.Valid {
323+
meanSecondsMetric = meanSeconds.Float64
324+
}
325+
ch <- prometheus.MustNewConstMetric(
326+
statStatementsMeanSeconds,
327+
prometheus.GaugeValue,
328+
meanSecondsMetric,
329+
userLabel, datnameLabel, queryidLabel,
330+
)
331+
332+
stddevSecondsMetric := 0.0
333+
if stddevSeconds.Valid {
334+
stddevSecondsMetric = stddevSeconds.Float64
335+
}
336+
ch <- prometheus.MustNewConstMetric(
337+
statStatementsStddevSeconds,
338+
prometheus.GaugeValue,
339+
stddevSecondsMetric,
340+
userLabel, datnameLabel, queryidLabel,
341+
)
342+
283343
if c.includeQueryStatement {
284344
_, ok := presentQueryIds[queryidLabel]
285345
if !ok {

collector/pg_stat_statements_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ func TestPGStateStatementsCollector(t *testing.T) {
3333

3434
inst := &instance{db: db, version: semver.MustParse("12.0.0")}
3535

36-
columns := []string{"user", "datname", "queryid", "calls_total", "seconds_total", "rows_total", "block_read_seconds_total", "block_write_seconds_total"}
36+
columns := []string{"user", "datname", "queryid", "calls_total", "seconds_total", "rows_total", "block_read_seconds_total", "block_write_seconds_total", "max_seconds", "mean_seconds", "stddev_seconds"}
3737
rows := sqlmock.NewRows(columns).
38-
AddRow("postgres", "postgres", 1500, 5, 0.4, 100, 0.1, 0.2)
38+
AddRow("postgres", "postgres", 1500, 5, 0.4, 100, 0.1, 0.2, 0.8, 0.08, 0.05)
3939
mock.ExpectQuery(sanitizeQuery(fmt.Sprintf(pgStatStatementsQuery, ""))).WillReturnRows(rows)
4040

4141
ch := make(chan prometheus.Metric)
@@ -207,9 +207,9 @@ func TestPGStateStatementsCollectorNewPG(t *testing.T) {
207207

208208
inst := &instance{db: db, version: semver.MustParse("13.3.7")}
209209

210-
columns := []string{"user", "datname", "queryid", "calls_total", "seconds_total", "rows_total", "block_read_seconds_total", "block_write_seconds_total"}
210+
columns := []string{"user", "datname", "queryid", "calls_total", "seconds_total", "rows_total", "block_read_seconds_total", "block_write_seconds_total", "max_seconds", "mean_seconds", "stddev_seconds"}
211211
rows := sqlmock.NewRows(columns).
212-
AddRow("postgres", "postgres", 1500, 5, 0.4, 100, 0.1, 0.2)
212+
AddRow("postgres", "postgres", 1500, 5, 0.4, 100, 0.1, 0.2, 0.8, 0.08, 0.05)
213213
mock.ExpectQuery(sanitizeQuery(fmt.Sprintf(pgStatStatementsNewQuery, ""))).WillReturnRows(rows)
214214

215215
ch := make(chan prometheus.Metric)
@@ -294,9 +294,9 @@ func TestPGStateStatementsCollector_PG17(t *testing.T) {
294294

295295
inst := &instance{db: db, version: semver.MustParse("17.0.0")}
296296

297-
columns := []string{"user", "datname", "queryid", "calls_total", "seconds_total", "rows_total", "block_read_seconds_total", "block_write_seconds_total"}
297+
columns := []string{"user", "datname", "queryid", "calls_total", "seconds_total", "rows_total", "block_read_seconds_total", "block_write_seconds_total", "max_seconds", "mean_seconds", "stddev_seconds"}
298298
rows := sqlmock.NewRows(columns).
299-
AddRow("postgres", "postgres", 1500, 5, 0.4, 100, 0.1, 0.2)
299+
AddRow("postgres", "postgres", 1500, 5, 0.4, 100, 0.1, 0.2, 0.8, 0.08, 0.05)
300300
mock.ExpectQuery(sanitizeQuery(fmt.Sprintf(pgStatStatementsQuery_PG17, ""))).WillReturnRows(rows)
301301

302302
ch := make(chan prometheus.Metric)

0 commit comments

Comments
 (0)