You've already forked postgres_exporter
mirror of
https://github.com/prometheus-community/postgres_exporter.git
synced 2026-10-23 12:08:57 +03:00
fix: skip autovacuum metric when xact_start is NULL (#1381)
The stat_activity_autovacuum collector scanned xact_start into a plain float64, so a NULL value (possible for autovacuum workers) failed the whole collector with "converting NULL to float64 is unsupported". Scan into sql.NullFloat64 and skip the row when it is NULL, matching the pg_stat_walreceiver / pg_stat_database collectors. Fixes #952 Signed-off-by: Dean Chen <862469039@qq.com>
This commit is contained in:
@@ -15,6 +15,7 @@ package collector
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"log/slog"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -51,7 +52,7 @@ var (
|
||||
`
|
||||
)
|
||||
|
||||
func (PGStatActivityAutovacuumCollector) Update(ctx context.Context, instance *instance, ch chan<- prometheus.Metric) error {
|
||||
func (c PGStatActivityAutovacuumCollector) Update(ctx context.Context, instance *instance, ch chan<- prometheus.Metric) error {
|
||||
db := instance.getDB()
|
||||
rows, err := db.QueryContext(ctx,
|
||||
statActivityAutovacuumQuery)
|
||||
@@ -63,16 +64,21 @@ func (PGStatActivityAutovacuumCollector) Update(ctx context.Context, instance *i
|
||||
|
||||
for rows.Next() {
|
||||
var relname string
|
||||
var ageInSeconds float64
|
||||
var ageInSeconds sql.NullFloat64
|
||||
|
||||
if err := rows.Scan(&relname, &ageInSeconds); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ageInSeconds.Valid {
|
||||
c.log.Debug("Skipping collecting metric because it has no xact_start")
|
||||
continue
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
statActivityAutovacuumAgeInSeconds,
|
||||
prometheus.GaugeValue,
|
||||
ageInSeconds, relname,
|
||||
ageInSeconds.Float64, relname,
|
||||
)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"github.com/prometheus/common/promslog"
|
||||
"github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
@@ -60,3 +61,43 @@ func TestPGStatActivityAutovacuumCollector(t *testing.T) {
|
||||
t.Errorf("there were unfulfilled exceptions: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPGStatActivityAutovacuumCollectorNullTimestamp(t *testing.T) {
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("Error opening a stub db connection: %s", err)
|
||||
}
|
||||
defer db.Close()
|
||||
inst := &instance{db: db}
|
||||
columns := []string{
|
||||
"relname",
|
||||
"timestamp_seconds",
|
||||
}
|
||||
rows := sqlmock.NewRows(columns).
|
||||
AddRow("no_xact_start", nil).
|
||||
AddRow("test", 3600)
|
||||
|
||||
mock.ExpectQuery(sanitizeQuery(statActivityAutovacuumQuery)).WillReturnRows(rows)
|
||||
|
||||
ch := make(chan prometheus.Metric)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
c := PGStatActivityAutovacuumCollector{log: promslog.NewNopLogger()}
|
||||
|
||||
if err := c.Update(context.Background(), inst, ch); err != nil {
|
||||
t.Errorf("Error calling PGStatActivityAutovacuumCollector.Update: %s", err)
|
||||
}
|
||||
}()
|
||||
expected := []MetricResult{
|
||||
{labels: labelMap{"relname": "test"}, value: 3600, metricType: dto.MetricType_GAUGE},
|
||||
}
|
||||
convey.Convey("Metrics comparison", t, func() {
|
||||
for _, expect := range expected {
|
||||
m := readMetric(<-ch)
|
||||
convey.So(expect, convey.ShouldResemble, m)
|
||||
}
|
||||
})
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("there were unfulfilled exceptions: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user