Skip to content

Commit 20dc930

Browse files
committed
Add roles connection limits metrics
Signed-off-by: Jocelyn Thode <[email protected]>
1 parent 6d7c22c commit 20dc930

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed

collector/pg_roles.go

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright 2022 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./go-kit/log"
21+
"github.com./prometheus/client_golang/prometheus"
22+
)
23+
24+
const rolesSubsystem = "roles"
25+
26+
func init() {
27+
registerCollector(rolesSubsystem, defaultEnabled, NewPGRolesCollector)
28+
}
29+
30+
type PGRolesCollector struct {
31+
log log.Logger
32+
}
33+
34+
func NewPGRolesCollector(config collectorConfig) (Collector, error) {
35+
return &PGRolesCollector{
36+
log: config.logger,
37+
}, nil
38+
}
39+
40+
var (
41+
pgRolesConnectionLimitsDesc = prometheus.NewDesc(
42+
prometheus.BuildFQName(
43+
namespace,
44+
rolesSubsystem,
45+
"connection_limit",
46+
),
47+
"Connection limit set for the role",
48+
[]string{"rolname"}, nil,
49+
)
50+
51+
pgRolesConnectionLimitsQuery = "select pg_roles.rolname,pg_roles.rolconnlimit FROM pg_roles"
52+
)
53+
54+
// Update implements Collector and exposes roles connection limits.
55+
// It is called by the Prometheus registry when collecting metrics.
56+
func (c PGRolesCollector) Update(ctx context.Context, instance *instance, ch chan<- prometheus.Metric) error {
57+
db := instance.getDB()
58+
// Query the list of databases
59+
rows, err := db.QueryContext(ctx,
60+
pgRolesConnectionLimitsQuery,
61+
)
62+
if err != nil {
63+
return err
64+
}
65+
defer rows.Close()
66+
67+
for rows.Next() {
68+
var rolname sql.NullString
69+
var connLimit sql.NullInt64
70+
if err := rows.Scan(&rolname, &connLimit); err != nil {
71+
return err
72+
}
73+
74+
rolnameLabel := "unknown"
75+
if rolname.Valid {
76+
rolnameLabel = rolname.String
77+
}
78+
79+
connLimitMetric := 0.0
80+
if connLimit.Valid {
81+
connLimitMetric = float64(connLimit.Int64)
82+
}
83+
84+
ch <- prometheus.MustNewConstMetric(
85+
pgRolesConnectionLimitsDesc,
86+
prometheus.GaugeValue, connLimitMetric, rolnameLabel,
87+
)
88+
}
89+
90+
return rows.Err()
91+
}

collector/pg_roles_test.go

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright 2023 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+
package collector
14+
15+
import (
16+
"context"
17+
"testing"
18+
19+
"github.com./DATA-DOG/go-sqlmock"
20+
"github.com./prometheus/client_golang/prometheus"
21+
dto "github.com./prometheus/client_model/go"
22+
"github.com./smartystreets/goconvey/convey"
23+
)
24+
25+
func TestPGRolesCollector(t *testing.T) {
26+
db, mock, err := sqlmock.New()
27+
if err != nil {
28+
t.Fatalf("Error opening a stub db connection: %s", err)
29+
}
30+
defer db.Close()
31+
32+
inst := &instance{db: db}
33+
34+
mock.ExpectQuery(sanitizeQuery(pgRolesConnectionLimitsQuery)).WillReturnRows(sqlmock.NewRows([]string{"rolname", "rolconnlimit"}).
35+
AddRow("postgres", 15))
36+
37+
ch := make(chan prometheus.Metric)
38+
go func() {
39+
defer close(ch)
40+
c := PGRolesCollector{}
41+
if err := c.Update(context.Background(), inst, ch); err != nil {
42+
t.Errorf("Error calling PGRolesCollector.Update: %s", err)
43+
}
44+
}()
45+
46+
expected := []MetricResult{
47+
{labels: labelMap{"rolname": "postgres"}, value: 15, metricType: dto.MetricType_GAUGE},
48+
}
49+
convey.Convey("Metrics comparison", t, func() {
50+
for _, expect := range expected {
51+
m := readMetric(<-ch)
52+
convey.So(expect, convey.ShouldResemble, m)
53+
}
54+
})
55+
if err := mock.ExpectationsWereMet(); err != nil {
56+
t.Errorf("there were unfulfilled exceptions: %s", err)
57+
}
58+
}
59+
60+
func TestPGRolesCollectorNullMetric(t *testing.T) {
61+
db, mock, err := sqlmock.New()
62+
if err != nil {
63+
t.Fatalf("Error opening a stub db connection: %s", err)
64+
}
65+
defer db.Close()
66+
67+
inst := &instance{db: db}
68+
69+
mock.ExpectQuery(sanitizeQuery(pgRolesConnectionLimitsQuery)).WillReturnRows(sqlmock.NewRows([]string{"rolname", "rolconnlimit"}).
70+
AddRow(nil, nil))
71+
72+
ch := make(chan prometheus.Metric)
73+
go func() {
74+
defer close(ch)
75+
c := PGRolesCollector{}
76+
77+
if err := c.Update(context.Background(), inst, ch); err != nil {
78+
t.Errorf("Error calling PGRolesCollector.Update: %s", err)
79+
}
80+
}()
81+
82+
expected := []MetricResult{
83+
{labels: labelMap{"rolname": "unknown"}, value: 0, metricType: dto.MetricType_GAUGE},
84+
}
85+
convey.Convey("Metrics comparison", t, func() {
86+
for _, expect := range expected {
87+
m := readMetric(<-ch)
88+
convey.So(expect, convey.ShouldResemble, m)
89+
}
90+
})
91+
if err := mock.ExpectationsWereMet(); err != nil {
92+
t.Errorf("there were unfulfilled exceptions: %s", err)
93+
}
94+
}

0 commit comments

Comments
 (0)