From 5a7e5258dd6986a84c9397ed05ef6746f35628b1 Mon Sep 17 00:00:00 2001 From: Honi Sanders Date: Tue, 5 May 2026 13:24:02 -0400 Subject: [PATCH] fix: handle NULL pg_stat_wal.stats_reset The stats_reset column of pg_stat_wal can be NULL on instances that have never had WAL stats initialized -- most commonly replicas (which do not write WAL stats locally) and primaries promoted from a replica that was bootstrapped via pg_basebackup. The exporter scanned this column into a plain string, so on such instances every collection failed with "sql: Scan error on column index 4, name stats_reset: converting NULL to string is unsupported", dropping all pg_stat_wal metrics for the pod and logging an error at every scrape interval. Scan stats_reset into sql.NullString and use the empty string as the metric label when the column is NULL. The actual WAL counters are then emitted normally. Closes #9106 Signed-off-by: Honi Sanders --- pkg/management/postgres/probes.go | 2 +- .../postgres/webserver/metricserver/wal.go | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/management/postgres/probes.go b/pkg/management/postgres/probes.go index 6b346c6346..930e6090bc 100644 --- a/pkg/management/postgres/probes.go +++ b/pkg/management/postgres/probes.go @@ -594,7 +594,7 @@ type PgStatWal struct { WalSync int64 WalWriteTime float64 WalSyncTime float64 - StatsReset string + StatsReset sql.NullString } // TryGetPgStatWAL retrieves pg_stat_wal on pg version 14 and further diff --git a/pkg/management/postgres/webserver/metricserver/wal.go b/pkg/management/postgres/webserver/metricserver/wal.go index f8921cb000..f293f9ffb8 100644 --- a/pkg/management/postgres/webserver/metricserver/wal.go +++ b/pkg/management/postgres/webserver/metricserver/wal.go @@ -50,15 +50,15 @@ func collectPGStatWAL(e *Exporter) error { return err } walMetrics := e.Metrics.PgStatWalMetrics - walMetrics.WalRecords.WithLabelValues(walStat.StatsReset).Set(float64(walStat.WalRecords)) - walMetrics.WalFpi.WithLabelValues(walStat.StatsReset).Set(float64(walStat.WalFpi)) - walMetrics.WalBytes.WithLabelValues(walStat.StatsReset).Set(float64(walStat.WalBytes)) - walMetrics.WALBuffersFull.WithLabelValues(walStat.StatsReset).Set(float64(walStat.WALBuffersFull)) + walMetrics.WalRecords.WithLabelValues(walStat.StatsReset.String).Set(float64(walStat.WalRecords)) + walMetrics.WalFpi.WithLabelValues(walStat.StatsReset.String).Set(float64(walStat.WalFpi)) + walMetrics.WalBytes.WithLabelValues(walStat.StatsReset.String).Set(float64(walStat.WalBytes)) + walMetrics.WALBuffersFull.WithLabelValues(walStat.StatsReset.String).Set(float64(walStat.WALBuffersFull)) if version, _ := e.instance.GetPgVersion(); version.Major < 18 { - walMetrics.WalWrite.WithLabelValues(walStat.StatsReset).Set(float64(walStat.WalWrite)) - walMetrics.WalSync.WithLabelValues(walStat.StatsReset).Set(float64(walStat.WalSync)) - walMetrics.WalWriteTime.WithLabelValues(walStat.StatsReset).Set(walStat.WalWriteTime) - walMetrics.WalSyncTime.WithLabelValues(walStat.StatsReset).Set(walStat.WalSyncTime) + walMetrics.WalWrite.WithLabelValues(walStat.StatsReset.String).Set(float64(walStat.WalWrite)) + walMetrics.WalSync.WithLabelValues(walStat.StatsReset.String).Set(float64(walStat.WalSync)) + walMetrics.WalWriteTime.WithLabelValues(walStat.StatsReset.String).Set(walStat.WalWriteTime) + walMetrics.WalSyncTime.WithLabelValues(walStat.StatsReset.String).Set(walStat.WalSyncTime) } return nil