Skip to content

Commit bb09c1a

Browse files
committed
Merge remote-tracking branch 'upstream/master' into alessio-upstream-sync
2 parents 30fdb25 + a4ac0e6 commit bb09c1a

17 files changed

+462
-101
lines changed

.circleci/config.yml

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
---
2+
version: 2.1
3+
4+
orbs:
5+
prometheus: prometheus/[email protected]
6+
7+
executors:
8+
# This must match .promu.yml.
9+
golang:
10+
docker:
11+
- image: cimg/go:1.21
12+
13+
jobs:
14+
test:
15+
executor: golang
16+
17+
steps:
18+
- prometheus/setup_environment
19+
- run: GOHOSTARCH=386 GOARCH=386 make test
20+
- run: make
21+
- prometheus/store_artifact:
22+
file: postgres_exporter
23+
24+
integration:
25+
docker:
26+
- image: cimg/go:1.20
27+
- image: << parameters.postgres_image >>
28+
environment:
29+
POSTGRES_DB: circle_test
30+
POSTGRES_USER: postgres
31+
POSTGRES_PASSWORD: test
32+
33+
parameters:
34+
postgres_image:
35+
type: string
36+
37+
environment:
38+
DATA_SOURCE_NAME: 'postgresql://postgres:test@localhost:5432/circle_test?sslmode=disable'
39+
GOOPTS: '-v -tags integration'
40+
41+
steps:
42+
- checkout
43+
- setup_remote_docker
44+
- run: docker version
45+
- run: make build
46+
- run: make test
47+
48+
workflows:
49+
version: 2
50+
postgres_exporter:
51+
jobs:
52+
- test:
53+
filters:
54+
tags:
55+
only: /.*/
56+
- integration:
57+
matrix:
58+
parameters:
59+
postgres_image:
60+
- circleci/postgres:11
61+
- circleci/postgres:12
62+
- circleci/postgres:13
63+
- cimg/postgres:14.9
64+
- cimg/postgres:15.4
65+
- cimg/postgres:16.0
66+
- prometheus/build:
67+
name: build
68+
parallelism: 3
69+
promu_opts: "-p linux/amd64 -p windows/amd64 -p linux/arm64 -p darwin/amd64 -p darwin/arm64 -p linux/386"
70+
filters:
71+
tags:
72+
ignore: /^v.*/
73+
branches:
74+
ignore: /^(main|master|release-.*|.*build-all.*)$/
75+
- prometheus/build:
76+
name: build_all
77+
parallelism: 12
78+
filters:
79+
branches:
80+
only: /^(main|master|release-.*|.*build-all.*)$/
81+
tags:
82+
only: /^v.*/
83+
- prometheus/publish_master:
84+
context: org-context
85+
docker_hub_organization: prometheuscommunity
86+
quay_io_organization: prometheuscommunity
87+
requires:
88+
- test
89+
- build_all
90+
filters:
91+
branches:
92+
only: master
93+
- prometheus/publish_release:
94+
context: org-context
95+
docker_hub_organization: prometheuscommunity
96+
quay_io_organization: prometheuscommunity
97+
requires:
98+
- test
99+
- build_all
100+
filters:
101+
tags:
102+
only: /^v.*/
103+
branches:
104+
ignore: /.*/

.github/workflows/golangci-lint.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ jobs:
1818
runs-on: ubuntu-latest
1919
steps:
2020
- name: Checkout repository
21-
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
21+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
2222
- name: install Go
2323
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
2424
with:
25-
go-version: 1.20.x
25+
go-version: 1.21.x
2626
- name: Install snmp_exporter/generator dependencies
2727
run: sudo apt-get update && sudo apt-get -y install libsnmp-dev
2828
if: github.repository == 'prometheus/snmp_exporter'

.promu.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
go:
22
# This must match .circle/config.yml.
3-
version: 1.20
3+
version: 1.21
44
repository:
55
path: github.com/form3tech-oss/postgres_exporter
66
build:
77
binaries:
88
- name: postgres_exporter
99
path: ./cmd/postgres_exporter
10-
flags: -a -tags 'netgo static_build'
1110
ldflags: |
1211
-X github.com/prometheus/common/version.Version={{.Version}}
1312
-X github.com/prometheus/common/version.Revision={{.Revision}}

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 0.15.0 / 2023-10-27
2+
3+
* [ENHANCEMENT] Add 1kB and 2kB units #915
4+
* [BUGFIX] Add error log when probe collector creation fails #918
5+
* [BUGFIX] Fix test build failures on 32-bit arch #919
6+
* [BUGFIX] Adjust collector to use separate connection per scrape #936
7+
18
## 0.14.0 / 2023-09-11
29

310
* [CHANGE] Add `state` label to pg_process_idle_seconds #862

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
Prometheus exporter for PostgreSQL server metrics.
99

10-
CI Tested PostgreSQL versions: `10`, `11`, `12`, `13`, `14`, `15`
10+
CI Tested PostgreSQL versions: `11`, `12`, `13`, `14`, `15`, `16`
1111

1212
## Quick Start
1313
This package is available for Docker:

VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.15.0

cmd/postgres_exporter/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func main() {
9393
}
9494

9595
excludedDatabases := strings.Split(*excludeDatabases, ",")
96-
logger.Log("msg", "Excluded databases", "databases", fmt.Sprintf("%v", excludedDatabases))
96+
level.Info(logger).Log("msg", "Excluded databases", "databases", fmt.Sprintf("%v", excludedDatabases))
9797

9898
if *queriesPath != "" {
9999
level.Warn(logger).Log("msg", "The extended queries.yaml config is DEPRECATED", "file", *queriesPath)

collector/pg_database.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,21 @@ var (
5353
"Disk space used by the database",
5454
[]string{"datname"}, nil,
5555
)
56+
pgDatabaseConnectionLimitsDesc = prometheus.NewDesc(
57+
prometheus.BuildFQName(
58+
namespace,
59+
databaseSubsystem,
60+
"connection_limit",
61+
),
62+
"Connection limit set for the database",
63+
[]string{"datname"}, nil,
64+
)
5665

57-
pgDatabaseQuery = "SELECT pg_database.datname FROM pg_database;"
66+
pgDatabaseQuery = "SELECT pg_database.datname, pg_database.datconnlimit FROM pg_database;"
5867
pgDatabaseSizeQuery = "SELECT pg_database_size($1)"
5968
)
6069

61-
// Update implements Collector and exposes database size.
70+
// Update implements Collector and exposes database size and connection limits.
6271
// It is called by the Prometheus registry when collecting metrics.
6372
// The list of databases is retrieved from pg_database and filtered
6473
// by the excludeDatabase config parameter. The tradeoff here is that
@@ -81,21 +90,32 @@ func (c PGDatabaseCollector) Update(ctx context.Context, instance *instance, ch
8190

8291
for rows.Next() {
8392
var datname sql.NullString
84-
if err := rows.Scan(&datname); err != nil {
93+
var connLimit sql.NullInt64
94+
if err := rows.Scan(&datname, &connLimit); err != nil {
8595
return err
8696
}
8797

8898
if !datname.Valid {
8999
continue
90100
}
101+
database := datname.String
91102
// Ignore excluded databases
92103
// Filtering is done here instead of in the query to avoid
93104
// a complicated NOT IN query with a variable number of parameters
94-
if sliceContains(c.excludedDatabases, datname.String) {
105+
if sliceContains(c.excludedDatabases, database) {
95106
continue
96107
}
97108

98-
databases = append(databases, datname.String)
109+
databases = append(databases, database)
110+
111+
connLimitMetric := 0.0
112+
if connLimit.Valid {
113+
connLimitMetric = float64(connLimit.Int64)
114+
}
115+
ch <- prometheus.MustNewConstMetric(
116+
pgDatabaseConnectionLimitsDesc,
117+
prometheus.GaugeValue, connLimitMetric, database,
118+
)
99119
}
100120

101121
// Query the size of the databases
@@ -114,6 +134,7 @@ func (c PGDatabaseCollector) Update(ctx context.Context, instance *instance, ch
114134
pgDatabaseSizeDesc,
115135
prometheus.GaugeValue, sizeMetric, datname,
116136
)
137+
117138
}
118139
return rows.Err()
119140
}

collector/pg_database_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ func TestPGDatabaseCollector(t *testing.T) {
3131

3232
inst := &instance{db: db}
3333

34-
mock.ExpectQuery(sanitizeQuery(pgDatabaseQuery)).WillReturnRows(sqlmock.NewRows([]string{"datname"}).
35-
AddRow("postgres"))
34+
mock.ExpectQuery(sanitizeQuery(pgDatabaseQuery)).WillReturnRows(sqlmock.NewRows([]string{"datname", "datconnlimit"}).
35+
AddRow("postgres", 15))
3636

3737
mock.ExpectQuery(sanitizeQuery(pgDatabaseSizeQuery)).WithArgs("postgres").WillReturnRows(sqlmock.NewRows([]string{"pg_database_size"}).
3838
AddRow(1024))
@@ -47,6 +47,7 @@ func TestPGDatabaseCollector(t *testing.T) {
4747
}()
4848

4949
expected := []MetricResult{
50+
{labels: labelMap{"datname": "postgres"}, value: 15, metricType: dto.MetricType_GAUGE},
5051
{labels: labelMap{"datname": "postgres"}, value: 1024, metricType: dto.MetricType_GAUGE},
5152
}
5253
convey.Convey("Metrics comparison", t, func() {
@@ -71,8 +72,8 @@ func TestPGDatabaseCollectorNullMetric(t *testing.T) {
7172

7273
inst := &instance{db: db}
7374

74-
mock.ExpectQuery(sanitizeQuery(pgDatabaseQuery)).WillReturnRows(sqlmock.NewRows([]string{"datname"}).
75-
AddRow("postgres"))
75+
mock.ExpectQuery(sanitizeQuery(pgDatabaseQuery)).WillReturnRows(sqlmock.NewRows([]string{"datname", "datconnlimit"}).
76+
AddRow("postgres", nil))
7677

7778
mock.ExpectQuery(sanitizeQuery(pgDatabaseSizeQuery)).WithArgs("postgres").WillReturnRows(sqlmock.NewRows([]string{"pg_database_size"}).
7879
AddRow(nil))
@@ -88,6 +89,7 @@ func TestPGDatabaseCollectorNullMetric(t *testing.T) {
8889

8990
expected := []MetricResult{
9091
{labels: labelMap{"datname": "postgres"}, value: 0, metricType: dto.MetricType_GAUGE},
92+
{labels: labelMap{"datname": "postgres"}, value: 0, metricType: dto.MetricType_GAUGE},
9193
}
9294
convey.Convey("Metrics comparison", t, func() {
9395
for _, expect := range expected {

collector/pg_replication_slot.go

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ var (
4343
"slot_current_wal_lsn",
4444
),
4545
"current wal lsn value",
46-
[]string{"slot_name"}, nil,
46+
[]string{"slot_name", "slot_type"}, nil,
4747
)
4848
pgReplicationSlotCurrentFlushDesc = prometheus.NewDesc(
4949
prometheus.BuildFQName(
@@ -52,7 +52,7 @@ var (
5252
"slot_confirmed_flush_lsn",
5353
),
5454
"last lsn confirmed flushed to the replication slot",
55-
[]string{"slot_name"}, nil,
55+
[]string{"slot_name", "slot_type"}, nil,
5656
)
5757
pgReplicationSlotIsActiveDesc = prometheus.NewDesc(
5858
prometheus.BuildFQName(
@@ -61,18 +61,39 @@ var (
6161
"slot_is_active",
6262
),
6363
"whether the replication slot is active or not",
64-
[]string{"slot_name"}, nil,
64+
[]string{"slot_name", "slot_type"}, nil,
65+
)
66+
pgReplicationSlotSafeWal = prometheus.NewDesc(
67+
prometheus.BuildFQName(
68+
namespace,
69+
replicationSlotSubsystem,
70+
"safe_wal_size_bytes",
71+
),
72+
"number of bytes that can be written to WAL such that this slot is not in danger of getting in state lost",
73+
[]string{"slot_name", "slot_type"}, nil,
74+
)
75+
pgReplicationSlotWalStatus = prometheus.NewDesc(
76+
prometheus.BuildFQName(
77+
namespace,
78+
replicationSlotSubsystem,
79+
"wal_status",
80+
),
81+
"availability of WAL files claimed by this slot",
82+
[]string{"slot_name", "slot_type", "wal_status"}, nil,
6583
)
6684

6785
pgReplicationSlotQuery = `SELECT
6886
slot_name,
69-
CASE WHEN pg_is_in_recovery() THEN
87+
slot_type,
88+
CASE WHEN pg_is_in_recovery() THEN
7089
pg_last_wal_receive_lsn() - '0/0'
71-
ELSE
72-
pg_current_wal_lsn() - '0/0'
90+
ELSE
91+
pg_current_wal_lsn() - '0/0'
7392
END AS current_wal_lsn,
74-
COALESCE(confirmed_flush_lsn, '0/0') - '0/0',
75-
active
93+
COALESCE(confirmed_flush_lsn, '0/0') - '0/0' AS confirmed_flush_lsn,
94+
active,
95+
safe_wal_size,
96+
wal_status
7697
FROM pg_replication_slots;`
7798
)
7899

@@ -87,10 +108,13 @@ func (PGReplicationSlotCollector) Update(ctx context.Context, instance *instance
87108

88109
for rows.Next() {
89110
var slotName sql.NullString
111+
var slotType sql.NullString
90112
var walLSN sql.NullFloat64
91113
var flushLSN sql.NullFloat64
92114
var isActive sql.NullBool
93-
if err := rows.Scan(&slotName, &walLSN, &flushLSN, &isActive); err != nil {
115+
var safeWalSize sql.NullInt64
116+
var walStatus sql.NullString
117+
if err := rows.Scan(&slotName, &slotType, &walLSN, &flushLSN, &isActive, &safeWalSize, &walStatus); err != nil {
94118
return err
95119
}
96120

@@ -102,14 +126,18 @@ func (PGReplicationSlotCollector) Update(ctx context.Context, instance *instance
102126
if slotName.Valid {
103127
slotNameLabel = slotName.String
104128
}
129+
slotTypeLabel := "unknown"
130+
if slotType.Valid {
131+
slotTypeLabel = slotType.String
132+
}
105133

106134
var walLSNMetric float64
107135
if walLSN.Valid {
108136
walLSNMetric = walLSN.Float64
109137
}
110138
ch <- prometheus.MustNewConstMetric(
111139
pgReplicationSlotCurrentWalDesc,
112-
prometheus.GaugeValue, walLSNMetric, slotNameLabel,
140+
prometheus.GaugeValue, walLSNMetric, slotNameLabel, slotTypeLabel,
113141
)
114142
if isActive.Valid && isActive.Bool {
115143
var flushLSNMetric float64
@@ -118,13 +146,27 @@ func (PGReplicationSlotCollector) Update(ctx context.Context, instance *instance
118146
}
119147
ch <- prometheus.MustNewConstMetric(
120148
pgReplicationSlotCurrentFlushDesc,
121-
prometheus.GaugeValue, flushLSNMetric, slotNameLabel,
149+
prometheus.GaugeValue, flushLSNMetric, slotNameLabel, slotTypeLabel,
122150
)
123151
}
124152
ch <- prometheus.MustNewConstMetric(
125153
pgReplicationSlotIsActiveDesc,
126-
prometheus.GaugeValue, isActiveValue, slotNameLabel,
154+
prometheus.GaugeValue, isActiveValue, slotNameLabel, slotTypeLabel,
127155
)
156+
157+
if safeWalSize.Valid {
158+
ch <- prometheus.MustNewConstMetric(
159+
pgReplicationSlotSafeWal,
160+
prometheus.GaugeValue, float64(safeWalSize.Int64), slotNameLabel, slotTypeLabel,
161+
)
162+
}
163+
164+
if walStatus.Valid {
165+
ch <- prometheus.MustNewConstMetric(
166+
pgReplicationSlotWalStatus,
167+
prometheus.GaugeValue, 1, slotNameLabel, slotTypeLabel, walStatus.String,
168+
)
169+
}
128170
}
129171
return rows.Err()
130172
}

0 commit comments

Comments
 (0)