Skip to content

Commit fea454c

Browse files
committed
PMM-7 Add PostgreSQL 18 support with new metrics and collectors
Introduce support for PostgreSQL 18, adding enhanced metrics for checkpointer, vacuum/analyze timings, and parallel worker activity. Implement new collectors for `pg_stat_io` and `pg_backend_memory_contexts` with version-specific queries, along with comprehensive tests. Update README and CI to reflect PostgreSQL 18 compatibility.
1 parent 8661208 commit fea454c

14 files changed

+1243
-24
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
## [Unreleased]
2+
3+
* [ENHANCEMENT] Add PostgreSQL 18 support:
4+
* Add parallel worker activity metrics (`pg_stat_database_parallel_workers_to_launch`, `pg_stat_database_parallel_workers_launched`)
5+
* Add vacuum/analyze timing metrics (`pg_stat_user_tables_total_vacuum_time`, `pg_stat_user_tables_total_autovacuum_time`, `pg_stat_user_tables_total_analyze_time`, `pg_stat_user_tables_total_autoanalyze_time`)
6+
* Add enhanced checkpointer metrics (`pg_stat_bgwriter_checkpoints_done_total`, `pg_stat_bgwriter_slru_written_total`)
7+
* Add `pg_stat_io` collector with byte statistics and WAL I/O activity tracking
8+
* Add `pg_backend_stats` collector for per-backend I/O and WAL statistics
9+
* Add enhanced `pg_backend_memory_contexts` collector with type and path columns
10+
* [ENHANCEMENT] Update CI tested PostgreSQL versions to include PostgreSQL 18
11+
112
## 0.15.0 / 2023-10-27
213

314
* [ENHANCEMENT] Add 1kB and 2kB units #915

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
Prometheus exporter for PostgreSQL server metrics.
99

10-
CI Tested PostgreSQL versions: `11`, `12`, `13`, `14`, `15`, `16`
10+
CI Tested PostgreSQL versions: `11`, `12`, `13`, `14`, `15`, `16`, `18`
1111

1212
## Quick Start
1313
This package is available for Docker:
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
// Copyright 2024 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package collector
15+
16+
import (
17+
"context"
18+
"database/sql"
19+
20+
"github.com/blang/semver/v4"
21+
"github.com/go-kit/log"
22+
"github.com/prometheus/client_golang/prometheus"
23+
)
24+
25+
const backendMemoryContextsSubsystem = "backend_memory_contexts"
26+
27+
func init() {
28+
registerCollector(backendMemoryContextsSubsystem, defaultDisabled, NewPGBackendMemoryContextsCollector)
29+
}
30+
31+
type PGBackendMemoryContextsCollector struct {
32+
log log.Logger
33+
}
34+
35+
func NewPGBackendMemoryContextsCollector(config collectorConfig) (Collector, error) {
36+
return &PGBackendMemoryContextsCollector{log: config.logger}, nil
37+
}
38+
39+
var (
40+
backendMemoryContextsTotalBytes = prometheus.NewDesc(
41+
prometheus.BuildFQName(namespace, backendMemoryContextsSubsystem, "total_bytes"),
42+
"Total bytes allocated for memory context",
43+
[]string{"pid", "name", "ident", "parent", "level", "type", "path"},
44+
prometheus.Labels{},
45+
)
46+
backendMemoryContextsUsedBytes = prometheus.NewDesc(
47+
prometheus.BuildFQName(namespace, backendMemoryContextsSubsystem, "used_bytes"),
48+
"Used bytes in memory context",
49+
[]string{"pid", "name", "ident", "parent", "level", "type", "path"},
50+
prometheus.Labels{},
51+
)
52+
backendMemoryContextsFreeBytes = prometheus.NewDesc(
53+
prometheus.BuildFQName(namespace, backendMemoryContextsSubsystem, "free_bytes"),
54+
"Free bytes in memory context",
55+
[]string{"pid", "name", "ident", "parent", "level", "type", "path"},
56+
prometheus.Labels{},
57+
)
58+
backendMemoryContextsFreeChunks = prometheus.NewDesc(
59+
prometheus.BuildFQName(namespace, backendMemoryContextsSubsystem, "free_chunks"),
60+
"Number of free chunks in memory context",
61+
[]string{"pid", "name", "ident", "parent", "level", "type", "path"},
62+
prometheus.Labels{},
63+
)
64+
65+
// PostgreSQL 18+ query with type and path columns
66+
backendMemoryContextsQuery18Plus = `
67+
SELECT
68+
pid,
69+
name,
70+
COALESCE(ident, '') as ident,
71+
COALESCE(parent, '') as parent,
72+
level,
73+
total_bytes,
74+
total_nblocks,
75+
free_bytes,
76+
free_chunks,
77+
used_bytes,
78+
type,
79+
path
80+
FROM pg_backend_memory_contexts
81+
ORDER BY pid, name
82+
`
83+
84+
// Pre-PostgreSQL 18 query without type and path columns
85+
backendMemoryContextsQueryPre18 = `
86+
SELECT
87+
pid,
88+
name,
89+
COALESCE(ident, '') as ident,
90+
COALESCE(parent, '') as parent,
91+
level,
92+
total_bytes,
93+
total_nblocks,
94+
free_bytes,
95+
free_chunks,
96+
used_bytes,
97+
'' as type,
98+
'' as path
99+
FROM pg_backend_memory_contexts
100+
ORDER BY pid, name
101+
`
102+
)
103+
104+
func (c *PGBackendMemoryContextsCollector) Update(ctx context.Context, instance *instance, ch chan<- prometheus.Metric) error {
105+
// pg_backend_memory_contexts was introduced in PostgreSQL 14
106+
if instance.version.LT(semver.Version{Major: 14}) {
107+
return nil
108+
}
109+
110+
db := instance.getDB()
111+
112+
// Use version-specific query for PostgreSQL 18+
113+
query := backendMemoryContextsQueryPre18
114+
if instance.version.GTE(semver.Version{Major: 18}) {
115+
query = backendMemoryContextsQuery18Plus
116+
}
117+
118+
rows, err := db.QueryContext(ctx, query)
119+
if err != nil {
120+
return err
121+
}
122+
defer rows.Close()
123+
124+
for rows.Next() {
125+
var pid, name, ident, parent, contextType, path sql.NullString
126+
var level, totalNblocks, freeChunks sql.NullInt64
127+
var totalBytes, freeBytes, usedBytes sql.NullFloat64
128+
129+
err := rows.Scan(
130+
&pid,
131+
&name,
132+
&ident,
133+
&parent,
134+
&level,
135+
&totalBytes,
136+
&totalNblocks,
137+
&freeBytes,
138+
&freeChunks,
139+
&usedBytes,
140+
&contextType,
141+
&path,
142+
)
143+
if err != nil {
144+
return err
145+
}
146+
147+
pidLabel := "unknown"
148+
if pid.Valid {
149+
pidLabel = pid.String
150+
}
151+
nameLabel := "unknown"
152+
if name.Valid {
153+
nameLabel = name.String
154+
}
155+
identLabel := ""
156+
if ident.Valid {
157+
identLabel = ident.String
158+
}
159+
parentLabel := ""
160+
if parent.Valid {
161+
parentLabel = parent.String
162+
}
163+
levelLabel := "0"
164+
if level.Valid {
165+
levelLabel = string(rune(level.Int64 + '0'))
166+
}
167+
typeLabel := ""
168+
if contextType.Valid {
169+
typeLabel = contextType.String
170+
}
171+
pathLabel := ""
172+
if path.Valid {
173+
pathLabel = path.String
174+
}
175+
176+
labels := []string{pidLabel, nameLabel, identLabel, parentLabel, levelLabel, typeLabel, pathLabel}
177+
178+
if totalBytes.Valid {
179+
ch <- prometheus.MustNewConstMetric(
180+
backendMemoryContextsTotalBytes,
181+
prometheus.GaugeValue,
182+
totalBytes.Float64,
183+
labels...,
184+
)
185+
}
186+
187+
if usedBytes.Valid {
188+
ch <- prometheus.MustNewConstMetric(
189+
backendMemoryContextsUsedBytes,
190+
prometheus.GaugeValue,
191+
usedBytes.Float64,
192+
labels...,
193+
)
194+
}
195+
196+
if freeBytes.Valid {
197+
ch <- prometheus.MustNewConstMetric(
198+
backendMemoryContextsFreeBytes,
199+
prometheus.GaugeValue,
200+
freeBytes.Float64,
201+
labels...,
202+
)
203+
}
204+
205+
if freeChunks.Valid {
206+
ch <- prometheus.MustNewConstMetric(
207+
backendMemoryContextsFreeChunks,
208+
prometheus.GaugeValue,
209+
float64(freeChunks.Int64),
210+
labels...,
211+
)
212+
}
213+
}
214+
215+
return nil
216+
}

0 commit comments

Comments
 (0)