Skip to content

Commit 3ecc4f7

Browse files
author
Anton Kucherov
committed
PMM-4131 Added conn_pool_stats metrics.
1 parent d4d1c28 commit 3ecc4f7

File tree

7 files changed

+185
-5
lines changed

7 files changed

+185
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- [PMM-4131](https://jira.percona.com/browse/PMM-4131): Added some features from [dcu/mongodb_exporter](https://github.com/dcu/mongodb_exporter). See list below.
1313
- New metrics:
1414
- `mongodb_mongod_replset_member_*`
15+
- `mongodb_connpoolstats_*`
1516

1617

1718
### Fixed

collector/common/conn_pool_stats.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package collector_common
2+
3+
import (
4+
"context"
5+
6+
"github.com/prometheus/client_golang/prometheus"
7+
"github.com/prometheus/common/log"
8+
"go.mongodb.org/mongo-driver/bson"
9+
"go.mongodb.org/mongo-driver/mongo"
10+
)
11+
12+
// server connections -- all of these!
13+
var (
14+
syncClientConnections = prometheus.NewGauge(prometheus.GaugeOpts{
15+
Namespace: Namespace,
16+
Subsystem: "connpoolstats",
17+
Name: "connection_sync",
18+
Help: "Corresponds to the total number of client connections to mongo.",
19+
})
20+
21+
numAScopedConnections = prometheus.NewGauge(prometheus.GaugeOpts{
22+
Namespace: Namespace,
23+
Subsystem: "connpoolstats",
24+
Name: "connections_scoped_sync",
25+
Help: "Corresponds to the number of active and stored outgoing scoped synchronous connections from the current instance to other members of the sharded cluster or replica set.",
26+
})
27+
28+
totalInUse = prometheus.NewGauge(prometheus.GaugeOpts{
29+
Namespace: Namespace,
30+
Subsystem: "connpoolstats",
31+
Name: "connections_in_use",
32+
Help: "Corresponds to the total number of client connections to mongo currently in use.",
33+
})
34+
35+
totalAvailable = prometheus.NewGauge(prometheus.GaugeOpts{
36+
Namespace: Namespace,
37+
Subsystem: "connpoolstats",
38+
Name: "connections_available",
39+
Help: "Corresponds to the total number of client connections to mongo that are currently available.",
40+
})
41+
42+
totalCreatedDesc = prometheus.NewDesc(
43+
prometheus.BuildFQName(Namespace, "connpoolstats", "connections_created_total"),
44+
"Corresponds to the total number of client connections to mongo created since instance start",
45+
nil,
46+
nil,
47+
)
48+
)
49+
50+
// ServerStatus keeps the data returned by the serverStatus() method.
51+
type ConnPoolStats struct {
52+
SyncClientConnections float64 `bson:"numClientConnections"`
53+
ASScopedConnections float64 `bson:"numAScopedConnections"`
54+
TotalInUse float64 `bson:"totalInUse"`
55+
TotalAvailable float64 `bson:"totalAvailable"`
56+
TotalCreated float64 `bson:"totalCreated"`
57+
}
58+
59+
// Export exports the server status to be consumed by prometheus.
60+
func (stats *ConnPoolStats) Export(ch chan<- prometheus.Metric) {
61+
syncClientConnections.Set(stats.SyncClientConnections)
62+
syncClientConnections.Collect(ch)
63+
64+
numAScopedConnections.Set(stats.ASScopedConnections)
65+
numAScopedConnections.Collect(ch)
66+
67+
totalInUse.Set(stats.TotalInUse)
68+
totalInUse.Collect(ch)
69+
70+
totalAvailable.Set(stats.TotalAvailable)
71+
totalAvailable.Collect(ch)
72+
73+
ch <- prometheus.MustNewConstMetric(totalCreatedDesc, prometheus.CounterValue, stats.TotalCreated)
74+
}
75+
76+
// Describe describes the server status for prometheus.
77+
func (stats *ConnPoolStats) Describe(ch chan<- *prometheus.Desc) {
78+
syncClientConnections.Describe(ch)
79+
numAScopedConnections.Describe(ch)
80+
totalInUse.Describe(ch)
81+
totalAvailable.Describe(ch)
82+
ch <- totalCreatedDesc
83+
}
84+
85+
// GetServerStatus returns the server status info.
86+
func GetConnPoolStats(client *mongo.Client) *ConnPoolStats {
87+
result := &ConnPoolStats{}
88+
err := client.Database("admin").RunCommand(context.TODO(), bson.D{{"connPoolStats", 1}, {"recordStats", 0}}).Decode(result)
89+
if err != nil {
90+
log.Error("Failed to get server status.")
91+
return nil
92+
}
93+
return result
94+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2017 Percona LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package collector_common
16+
17+
import (
18+
"context"
19+
"testing"
20+
"time"
21+
22+
"github.com/stretchr/testify/assert"
23+
24+
"github.com/percona/mongodb_exporter/testutils"
25+
)
26+
27+
func TestGetConnPoolStatsDecodesFine(t *testing.T) {
28+
// setup
29+
t.Parallel()
30+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
31+
defer cancel()
32+
33+
t.Run("mongod", func(t *testing.T) {
34+
// setup
35+
defaultClient := testutils.MustGetConnectedMongodClient(t, ctx)
36+
defer defaultClient.Disconnect(ctx)
37+
// run
38+
statusDefault := GetConnPoolStats(defaultClient)
39+
// test
40+
assert.NotNil(t, statusDefault)
41+
})
42+
43+
t.Run("replset", func(t *testing.T) {
44+
// setup
45+
replSetClient := testutils.MustGetConnectedReplSetClient(t, ctx)
46+
defer replSetClient.Disconnect(ctx)
47+
// run
48+
statusReplSet := GetConnPoolStats(replSetClient)
49+
// test
50+
assert.NotNil(t, statusReplSet)
51+
})
52+
}

collector/mongodb_collector.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/prometheus/common/log"
2525
"go.mongodb.org/mongo-driver/mongo"
2626

27+
collector_common "github.com/percona/mongodb_exporter/collector/common"
2728
"github.com/percona/mongodb_exporter/collector/mongod"
2829
"github.com/percona/mongodb_exporter/collector/mongos"
2930
"github.com/percona/mongodb_exporter/shared"
@@ -44,6 +45,7 @@ type MongodbCollectorOpts struct {
4445
CollectCollectionMetrics bool
4546
CollectTopMetrics bool
4647
CollectIndexUsageStats bool
48+
CollectConnPoolStats bool
4749
SocketTimeout time.Duration
4850
SyncTimeout time.Duration
4951
AuthentificationDB string
@@ -237,7 +239,6 @@ func (exporter *MongodbCollector) scrape(ch chan<- prometheus.Metric) {
237239

238240
func (exporter *MongodbCollector) collectMongos(client *mongo.Client, ch chan<- prometheus.Metric) {
239241
log.Debug("Collecting Server Status")
240-
241242
serverStatus := mongos.GetServerStatus(client)
242243
if serverStatus != nil {
243244
serverStatus.Export(ch)
@@ -264,6 +265,14 @@ func (exporter *MongodbCollector) collectMongos(client *mongo.Client, ch chan<-
264265
collStatList.Export(ch)
265266
}
266267
}
268+
269+
if exporter.Opts.CollectConnPoolStats {
270+
log.Debug("Collecting ConnPoolStats Metrics")
271+
connPoolStats := collector_common.GetConnPoolStats(client)
272+
if connPoolStats != nil {
273+
connPoolStats.Export(ch)
274+
}
275+
}
267276
}
268277

269278
func (exporter *MongodbCollector) collectMongod(client *mongo.Client, ch chan<- prometheus.Metric) {
@@ -304,6 +313,14 @@ func (exporter *MongodbCollector) collectMongod(client *mongo.Client, ch chan<-
304313
indexStatList.Export(ch)
305314
}
306315
}
316+
317+
if exporter.Opts.CollectConnPoolStats {
318+
log.Debug("Collecting ConnPoolStats Metrics")
319+
connPoolStats := collector_common.GetConnPoolStats(client)
320+
if connPoolStats != nil {
321+
connPoolStats.Export(ch)
322+
}
323+
}
307324
}
308325

309326
func (exporter *MongodbCollector) collectMongodReplSet(client *mongo.Client, ch chan<- prometheus.Metric) {

mongodb_exporter.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@ var (
4242
listenAddressF = kingpin.Flag("web.listen-address", "Address to listen on for web interface and telemetry.").Default(":9216").String()
4343
metricsPathF = kingpin.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").String()
4444

45-
collectDatabaseF = kingpin.Flag("collect.database", "Enable collection of Database metrics").Bool()
46-
collectCollectionF = kingpin.Flag("collect.collection", "Enable collection of Collection metrics").Bool()
47-
collectTopF = kingpin.Flag("collect.topmetrics", "Enable collection of table top metrics").Bool()
48-
collectIndexUsageF = kingpin.Flag("collect.indexusage", "Enable collection of per index usage stats").Bool()
45+
collectDatabaseF = kingpin.Flag("collect.database", "Enable collection of Database metrics").Bool()
46+
collectCollectionF = kingpin.Flag("collect.collection", "Enable collection of Collection metrics").Bool()
47+
collectTopF = kingpin.Flag("collect.topmetrics", "Enable collection of table top metrics").Bool()
48+
collectIndexUsageF = kingpin.Flag("collect.indexusage", "Enable collection of per index usage stats").Bool()
49+
mongodbCollectConnPoolStats = kingpin.Flag("collect.connpoolstats", "Collect MongoDB connpoolstats").Bool()
4950

5051
uriF = kingpin.Flag("mongodb.uri", "MongoDB URI, format").
5152
PlaceHolder("[mongodb://][user:pass@]host1[:port1][,host2[:port2],...][/database][?options]").
@@ -120,6 +121,7 @@ func main() {
120121
CollectCollectionMetrics: *collectCollectionF,
121122
CollectTopMetrics: *collectTopF,
122123
CollectIndexUsageStats: *collectIndexUsageF,
124+
CollectConnPoolStats: *mongodbCollectConnPoolStats,
123125
SocketTimeout: *socketTimeoutF,
124126
SyncTimeout: *syncTimeoutF,
125127
AuthentificationDB: *authDB,

testdata/mongodb_exporter.testFlagHelp.golden

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Flags:
2323
--collect.collection Enable collection of Collection metrics
2424
--collect.topmetrics Enable collection of table top metrics
2525
--collect.indexusage Enable collection of per index usage stats
26+
--collect.connpoolstats Collect MongoDB connpoolstats
2627
--mongodb.uri=[mongodb://][user:pass@]host1[:port1][,host2[:port2],...][/database][?options]
2728
MongoDB URI, format
2829
--mongodb.authentification-database=""

testutils/mongo_client.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,16 @@ func MustGetConnectedReplSetClient(t *testing.T, ctx context.Context) *mongo.Cli
2323

2424
return client
2525
}
26+
27+
// MustGetConnectedMongodClient return mongo.Client instance connected to server started in single mode.
28+
func MustGetConnectedMongodClient(t *testing.T, ctx context.Context) *mongo.Client {
29+
opts := options.Client().
30+
ApplyURI("mongodb://127.0.0.1:27017/admin").
31+
SetDirect(true).SetServerSelectionTimeout(time.Second)
32+
client, err := mongo.Connect(ctx, opts)
33+
if err != nil {
34+
t.Fatal(errors.Wrap(err, "Couldn't connect to MongoDB instance"))
35+
}
36+
37+
return client
38+
}

0 commit comments

Comments
 (0)