Skip to content

Commit 9e42fc0

Browse files
fix: handle pg_replication_slots on pg<13 (#1098)
* fix: handle pg_replication_slots on pg<13 Signed-off-by: Michael Todorovic <[email protected]> * fix: tests Signed-off-by: Michael Todorovic <[email protected]> --------- Signed-off-by: Michael Todorovic <[email protected]>
1 parent 072864d commit 9e42fc0

File tree

2 files changed

+44
-11
lines changed

2 files changed

+44
-11
lines changed

collector/pg_replication_slot.go

+35-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"database/sql"
1919
"log/slog"
2020

21+
"github.com/blang/semver/v4"
2122
"github.com/prometheus/client_golang/prometheus"
2223
)
2324

@@ -81,8 +82,18 @@ var (
8182
"availability of WAL files claimed by this slot",
8283
[]string{"slot_name", "slot_type", "wal_status"}, nil,
8384
)
84-
8585
pgReplicationSlotQuery = `SELECT
86+
slot_name,
87+
slot_type,
88+
CASE WHEN pg_is_in_recovery() THEN
89+
pg_last_wal_receive_lsn() - '0/0'
90+
ELSE
91+
pg_current_wal_lsn() - '0/0'
92+
END AS current_wal_lsn,
93+
COALESCE(confirmed_flush_lsn, '0/0') - '0/0' AS confirmed_flush_lsn,
94+
active
95+
FROM pg_replication_slots;`
96+
pgReplicationSlotNewQuery = `SELECT
8697
slot_name,
8798
slot_type,
8899
CASE WHEN pg_is_in_recovery() THEN
@@ -98,9 +109,15 @@ var (
98109
)
99110

100111
func (PGReplicationSlotCollector) Update(ctx context.Context, instance *instance, ch chan<- prometheus.Metric) error {
112+
query := pgReplicationSlotQuery
113+
abovePG13 := instance.version.GTE(semver.MustParse("13.0.0"))
114+
if abovePG13 {
115+
query = pgReplicationSlotNewQuery
116+
}
117+
101118
db := instance.getDB()
102119
rows, err := db.QueryContext(ctx,
103-
pgReplicationSlotQuery)
120+
query)
104121
if err != nil {
105122
return err
106123
}
@@ -114,7 +131,22 @@ func (PGReplicationSlotCollector) Update(ctx context.Context, instance *instance
114131
var isActive sql.NullBool
115132
var safeWalSize sql.NullInt64
116133
var walStatus sql.NullString
117-
if err := rows.Scan(&slotName, &slotType, &walLSN, &flushLSN, &isActive, &safeWalSize, &walStatus); err != nil {
134+
135+
r := []any{
136+
&slotName,
137+
&slotType,
138+
&walLSN,
139+
&flushLSN,
140+
&isActive,
141+
}
142+
143+
if abovePG13 {
144+
r = append(r, &safeWalSize)
145+
r = append(r, &walStatus)
146+
}
147+
148+
err := rows.Scan(r...)
149+
if err != nil {
118150
return err
119151
}
120152

collector/pg_replication_slot_test.go

+9-8
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"testing"
1818

1919
"github.com/DATA-DOG/go-sqlmock"
20+
"github.com/blang/semver/v4"
2021
"github.com/prometheus/client_golang/prometheus"
2122
dto "github.com/prometheus/client_model/go"
2223
"github.com/smartystreets/goconvey/convey"
@@ -29,12 +30,12 @@ func TestPgReplicationSlotCollectorActive(t *testing.T) {
2930
}
3031
defer db.Close()
3132

32-
inst := &instance{db: db}
33+
inst := &instance{db: db, version: semver.MustParse("13.3.7")}
3334

3435
columns := []string{"slot_name", "slot_type", "current_wal_lsn", "confirmed_flush_lsn", "active", "safe_wal_size", "wal_status"}
3536
rows := sqlmock.NewRows(columns).
3637
AddRow("test_slot", "physical", 5, 3, true, 323906992, "reserved")
37-
mock.ExpectQuery(sanitizeQuery(pgReplicationSlotQuery)).WillReturnRows(rows)
38+
mock.ExpectQuery(sanitizeQuery(pgReplicationSlotNewQuery)).WillReturnRows(rows)
3839

3940
ch := make(chan prometheus.Metric)
4041
go func() {
@@ -72,12 +73,12 @@ func TestPgReplicationSlotCollectorInActive(t *testing.T) {
7273
}
7374
defer db.Close()
7475

75-
inst := &instance{db: db}
76+
inst := &instance{db: db, version: semver.MustParse("13.3.7")}
7677

7778
columns := []string{"slot_name", "slot_type", "current_wal_lsn", "confirmed_flush_lsn", "active", "safe_wal_size", "wal_status"}
7879
rows := sqlmock.NewRows(columns).
7980
AddRow("test_slot", "physical", 6, 12, false, -4000, "extended")
80-
mock.ExpectQuery(sanitizeQuery(pgReplicationSlotQuery)).WillReturnRows(rows)
81+
mock.ExpectQuery(sanitizeQuery(pgReplicationSlotNewQuery)).WillReturnRows(rows)
8182

8283
ch := make(chan prometheus.Metric)
8384
go func() {
@@ -115,12 +116,12 @@ func TestPgReplicationSlotCollectorActiveNil(t *testing.T) {
115116
}
116117
defer db.Close()
117118

118-
inst := &instance{db: db}
119+
inst := &instance{db: db, version: semver.MustParse("13.3.7")}
119120

120121
columns := []string{"slot_name", "slot_type", "current_wal_lsn", "confirmed_flush_lsn", "active", "safe_wal_size", "wal_status"}
121122
rows := sqlmock.NewRows(columns).
122123
AddRow("test_slot", "physical", 6, 12, nil, nil, "lost")
123-
mock.ExpectQuery(sanitizeQuery(pgReplicationSlotQuery)).WillReturnRows(rows)
124+
mock.ExpectQuery(sanitizeQuery(pgReplicationSlotNewQuery)).WillReturnRows(rows)
124125

125126
ch := make(chan prometheus.Metric)
126127
go func() {
@@ -156,12 +157,12 @@ func TestPgReplicationSlotCollectorTestNilValues(t *testing.T) {
156157
}
157158
defer db.Close()
158159

159-
inst := &instance{db: db}
160+
inst := &instance{db: db, version: semver.MustParse("13.3.7")}
160161

161162
columns := []string{"slot_name", "slot_type", "current_wal_lsn", "confirmed_flush_lsn", "active", "safe_wal_size", "wal_status"}
162163
rows := sqlmock.NewRows(columns).
163164
AddRow(nil, nil, nil, nil, true, nil, nil)
164-
mock.ExpectQuery(sanitizeQuery(pgReplicationSlotQuery)).WillReturnRows(rows)
165+
mock.ExpectQuery(sanitizeQuery(pgReplicationSlotNewQuery)).WillReturnRows(rows)
165166

166167
ch := make(chan prometheus.Metric)
167168
go func() {

0 commit comments

Comments
 (0)