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
Add aurora_global_db_instance_status collector for Amazon Aurora PostgreSQL (#1411)
Expose how far each instance, including the cross-Region replicas of an Aurora global database, lags behind the writer, from aurora_global_db_instance_status(), converted to seconds. The writer reports NULL lag, so no lag is exported for it. aws_region is a label because the same server_id can appear in several Regions. The collector is disabled by default and returns no data on non-Aurora servers. Aurora collector names get their own block in config.go, so gofmt does not realign the main list when longer names are added. Signed-off-by: Pavel K <megativ3@gmail.com>
This commit is contained in:
@@ -43,6 +43,7 @@ the `-file` flags at a mounted secret.
|
||||
* [FEATURE] Add `aurora_stat_logical_wal_cache` collector for Amazon Aurora PostgreSQL by @megative in https://github.com/prometheus-community/postgres_exporter/pull/1414
|
||||
* [BUGFIX] Fix the `replication` collector failing on every scrape on Amazon Aurora PostgreSQL, where it now reports only `pg_replication_is_replica`, by @megative in https://github.com/prometheus-community/postgres_exporter/pull/1424, based on the contribution by @dannotripp in https://github.com/prometheus-community/postgres_exporter/pull/1274
|
||||
* [FEATURE] Add `aurora_stat_database` collector for Amazon Aurora PostgreSQL by @megative in https://github.com/prometheus-community/postgres_exporter/pull/1425
|
||||
* [FEATURE] Add `aurora_global_db_instance_status` collector for Amazon Aurora PostgreSQL by @megative in https://github.com/prometheus-community/postgres_exporter/pull/1411
|
||||
|
||||
## 0.20.1 / 2026-07-07
|
||||
|
||||
|
||||
@@ -103,6 +103,9 @@ This will build the docker image as `prometheuscommunity/postgres_exporter:${bra
|
||||
Show context-sensitive help (also try --help-long and --help-man).
|
||||
|
||||
|
||||
* `[no-]collector.aurora_global_db_instance_status`
|
||||
Enable the `aurora_global_db_instance_status` collector (default: disabled).
|
||||
|
||||
* `[no-]collector.aurora_global_db_status`
|
||||
Enable the `aurora_global_db_status` collector (default: disabled).
|
||||
|
||||
|
||||
87
collector/aurora_global_db_instance_status.go
Normal file
87
collector/aurora_global_db_instance_status.go
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package collector
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const auroraGlobalDBInstanceStatusSubsystem = "aurora_global_db_instance_status"
|
||||
|
||||
func init() {
|
||||
registerCollector(auroraGlobalDBInstanceStatusSubsystem, serverScope, NewAuroraGlobalDBInstanceStatusCollector)
|
||||
}
|
||||
|
||||
// AuroraGlobalDBInstanceStatusCollector exposes metrics from Amazon Aurora
|
||||
// PostgreSQL's aurora_global_db_instance_status() function: how far each
|
||||
// instance, including the cross-Region replicas of an Aurora global
|
||||
// database, lags behind the writer. It reports no data on servers that are
|
||||
// not Aurora.
|
||||
type AuroraGlobalDBInstanceStatusCollector struct{}
|
||||
|
||||
func NewAuroraGlobalDBInstanceStatusCollector(collectorConfig) (Collector, error) {
|
||||
return &AuroraGlobalDBInstanceStatusCollector{}, nil
|
||||
}
|
||||
|
||||
var (
|
||||
auroraGlobalDBInstanceStatusVisibilityLag = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, auroraGlobalDBInstanceStatusSubsystem, "visibility_lag_seconds"),
|
||||
"How far the instance lags behind the writer instance in seconds.",
|
||||
[]string{"server_id", "aws_region"}, nil,
|
||||
)
|
||||
|
||||
auroraGlobalDBInstanceStatusQuery = `SELECT
|
||||
server_id,
|
||||
aws_region,
|
||||
visibility_lag_in_msec
|
||||
FROM aurora_global_db_instance_status()`
|
||||
)
|
||||
|
||||
func (c AuroraGlobalDBInstanceStatusCollector) Update(ctx context.Context, instance *instance, ch chan<- prometheus.Metric) error {
|
||||
if !instance.isAurora {
|
||||
return ErrNoData
|
||||
}
|
||||
rows, err := instance.getDB().QueryContext(ctx, auroraGlobalDBInstanceStatusQuery)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var found bool
|
||||
for rows.Next() {
|
||||
found = true
|
||||
|
||||
var serverID, awsRegion string
|
||||
var visibilityLagMsec sql.NullFloat64
|
||||
|
||||
if err := rows.Scan(&serverID, &awsRegion, &visibilityLagMsec); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The writer reports NULL: it doesn't lag behind itself.
|
||||
if visibilityLagMsec.Valid {
|
||||
ch <- prometheus.MustNewConstMetric(auroraGlobalDBInstanceStatusVisibilityLag, prometheus.GaugeValue, visibilityLagMsec.Float64/1e3, serverID, awsRegion)
|
||||
}
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
return ErrNoData
|
||||
}
|
||||
return nil
|
||||
}
|
||||
124
collector/aurora_global_db_instance_status_test.go
Normal file
124
collector/aurora_global_db_instance_status_test.go
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package collector
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
var auroraGlobalDBInstanceStatusColumns = []string{
|
||||
"server_id",
|
||||
"aws_region",
|
||||
"visibility_lag_in_msec",
|
||||
}
|
||||
|
||||
func TestAuroraGlobalDBInstanceStatusCollector(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, isAurora: true}
|
||||
|
||||
// The writer reports NULL visibility lag, so nothing is emitted for it.
|
||||
// The same server_id can appear in several Regions.
|
||||
rows := sqlmock.NewRows(auroraGlobalDBInstanceStatusColumns).
|
||||
AddRow("instance-01", "eu-west-1", nil).
|
||||
AddRow("instance-02", "eu-west-1", 6).
|
||||
AddRow("global-instance-1", "eu-central-1", 996).
|
||||
AddRow("global-instance-1", "eu-west-2", 14)
|
||||
mock.ExpectQuery(sanitizeQuery(auroraGlobalDBInstanceStatusQuery)).WillReturnRows(rows)
|
||||
|
||||
ch := make(chan prometheus.Metric)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
c := AuroraGlobalDBInstanceStatusCollector{}
|
||||
if err := c.Update(context.Background(), inst, ch); err != nil {
|
||||
t.Errorf("Error calling AuroraGlobalDBInstanceStatusCollector.Update: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
expected := []struct {
|
||||
desc *prometheus.Desc
|
||||
result MetricResult
|
||||
}{
|
||||
{auroraGlobalDBInstanceStatusVisibilityLag, MetricResult{labels: labelMap{"server_id": "instance-02", "aws_region": "eu-west-1"}, value: 0.006, metricType: dto.MetricType_GAUGE}},
|
||||
{auroraGlobalDBInstanceStatusVisibilityLag, MetricResult{labels: labelMap{"server_id": "global-instance-1", "aws_region": "eu-central-1"}, value: 0.996, metricType: dto.MetricType_GAUGE}},
|
||||
{auroraGlobalDBInstanceStatusVisibilityLag, MetricResult{labels: labelMap{"server_id": "global-instance-1", "aws_region": "eu-west-2"}, value: 0.014, metricType: dto.MetricType_GAUGE}},
|
||||
}
|
||||
convey.Convey("Metrics comparison", t, func() {
|
||||
for _, expect := range expected {
|
||||
m := <-ch
|
||||
convey.So(m.Desc(), convey.ShouldEqual, expect.desc)
|
||||
convey.So(readMetric(m), convey.ShouldResemble, expect.result)
|
||||
}
|
||||
_, more := <-ch
|
||||
convey.So(more, convey.ShouldBeFalse)
|
||||
})
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("there were unfulfilled exceptions: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuroraGlobalDBInstanceStatusCollectorNotAurora(t *testing.T) {
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("Error opening a stub db connection: %s", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// No query is expected: the collector must not touch the database when
|
||||
// the server is not Aurora.
|
||||
inst := &instance{db: db, isAurora: false}
|
||||
|
||||
ch := make(chan prometheus.Metric, 1)
|
||||
c := AuroraGlobalDBInstanceStatusCollector{}
|
||||
if err := c.Update(context.Background(), inst, ch); err != ErrNoData {
|
||||
t.Errorf("Expected ErrNoData on non-Aurora server, got: %v", err)
|
||||
}
|
||||
if len(ch) != 0 {
|
||||
t.Errorf("Expected no metrics on non-Aurora server, got %d", len(ch))
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("there were unfulfilled exceptions: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuroraGlobalDBInstanceStatusCollectorNoRows(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, isAurora: true}
|
||||
|
||||
mock.ExpectQuery(sanitizeQuery(auroraGlobalDBInstanceStatusQuery)).WillReturnRows(sqlmock.NewRows(auroraGlobalDBInstanceStatusColumns))
|
||||
|
||||
ch := make(chan prometheus.Metric, 1)
|
||||
c := AuroraGlobalDBInstanceStatusCollector{}
|
||||
if err := c.Update(context.Background(), inst, ch); err != ErrNoData {
|
||||
t.Errorf("Expected ErrNoData, got: %v", err)
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("there were unfulfilled exceptions: %s", err)
|
||||
}
|
||||
}
|
||||
@@ -73,10 +73,11 @@ const (
|
||||
CollectorXlogLocation = "xlog_location"
|
||||
|
||||
// Amazon Aurora PostgreSQL collectors.
|
||||
CollectorAuroraGlobalDBStatus = "aurora_global_db_status"
|
||||
CollectorAuroraReplicaStatus = "aurora_replica_status"
|
||||
CollectorAuroraStatDatabase = "aurora_stat_database"
|
||||
CollectorAuroraStatLogicalWalCache = "aurora_stat_logical_wal_cache"
|
||||
CollectorAuroraGlobalDBInstanceStatus = "aurora_global_db_instance_status"
|
||||
CollectorAuroraGlobalDBStatus = "aurora_global_db_status"
|
||||
CollectorAuroraReplicaStatus = "aurora_replica_status"
|
||||
CollectorAuroraStatDatabase = "aurora_stat_database"
|
||||
CollectorAuroraStatLogicalWalCache = "aurora_stat_logical_wal_cache"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
@@ -235,10 +236,11 @@ func DefaultCollectorConfig() map[string]bool {
|
||||
CollectorXlogLocation: false,
|
||||
|
||||
// Amazon Aurora PostgreSQL collectors.
|
||||
CollectorAuroraGlobalDBStatus: false,
|
||||
CollectorAuroraReplicaStatus: false,
|
||||
CollectorAuroraStatDatabase: false,
|
||||
CollectorAuroraStatLogicalWalCache: false,
|
||||
CollectorAuroraGlobalDBInstanceStatus: false,
|
||||
CollectorAuroraGlobalDBStatus: false,
|
||||
CollectorAuroraReplicaStatus: false,
|
||||
CollectorAuroraStatDatabase: false,
|
||||
CollectorAuroraStatLogicalWalCache: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,8 +64,9 @@ and `datname` labels; update dashboards and alerts to use the names without
|
||||
| `wal` | enabled | Number of WAL segment files and their total size on disk, via `pg_ls_waldir()`. On Amazon Aurora PostgreSQL versions that don't support `pg_ls_waldir()`, it reports nothing. |
|
||||
| `stat_wal_receiver` | disabled | Seen from a standby: the WAL receiver's LSNs, timeline, upstream node, and last message send/receipt times. Enable this on replicas for a view of the receiving side. From `pg_stat_wal_receiver`. |
|
||||
| `xlog_location` | disabled | Current WAL write position (or replay position on a replica) as a byte offset. Superseded by `stat_replication`; retained for old setups and pre-10 servers. |
|
||||
| `aurora_replica_status` | disabled | **Amazon Aurora PostgreSQL only.** For each instance in the cluster: replica lag behind the writer, replay latency, and pending page reads. On non-Aurora servers it reports nothing. From `aurora_replica_status()`. |
|
||||
| `aurora_global_db_instance_status` | disabled | **Amazon Aurora PostgreSQL only.** For each instance, including the cross-Region replicas of an Aurora global database: how far it lags behind the writer, in seconds. On non-Aurora servers it reports nothing. From `aurora_global_db_instance_status()`. |
|
||||
| `aurora_global_db_status` | disabled | **Amazon Aurora PostgreSQL only.** For each secondary cluster of an Aurora global database, by AWS Region: storage (durability) lag and recovery point objective (RPO) lag behind the primary cluster, in seconds. On non-Aurora servers it reports nothing. From `aurora_global_db_status()`. |
|
||||
| `aurora_replica_status` | disabled | **Amazon Aurora PostgreSQL only.** For each instance in the cluster: replica lag behind the writer, replay latency, and pending page reads. On non-Aurora servers it reports nothing. From `aurora_replica_status()`. |
|
||||
| `aurora_stat_logical_wal_cache` | disabled | **Amazon Aurora PostgreSQL only.** Per logical replication slot: WAL cache hits, misses, and read requests. Requires Aurora PostgreSQL 11.17, 12.12, 13.8, 14.7, 15.2 or later; on older versions and on non-Aurora servers it reports nothing. From `aurora_stat_logical_wal_cache()`. |
|
||||
|
||||
### Checkpoints, buffers, and I/O
|
||||
|
||||
Reference in New Issue
Block a user