Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions collector/mongodb_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
package collector

import (
"errors"
"fmt"
"sync"
"time"

"github.com/prometheus/client_golang/prometheus"
Expand All @@ -38,6 +38,7 @@ type MongodbCollectorOpts struct {
TLSPrivateKeyFile string
TLSCaFile string
TLSHostnameValidation bool
DbPoolLimit int
}

func (in MongodbCollectorOpts) toSessionOps() shared.MongoSessionOpts {
Expand All @@ -48,6 +49,7 @@ func (in MongodbCollectorOpts) toSessionOps() shared.MongoSessionOpts {
TLSPrivateKeyFile: in.TLSPrivateKeyFile,
TLSCaFile: in.TLSCaFile,
TLSHostnameValidation: in.TLSHostnameValidation,
PoolLimit: in.DbPoolLimit,
}
}

Expand All @@ -57,6 +59,8 @@ type MongodbCollector struct {
scrapesTotal prometheus.Counter
lastScrapeError prometheus.Gauge
lastScrapeDurationSeconds prometheus.Gauge
mongoSess *mgo.Session
mongoSessLock sync.Mutex
}

// NewMongodbCollector returns a new instance of a MongodbCollector.
Expand Down Expand Up @@ -87,6 +91,31 @@ func NewMongodbCollector(opts MongodbCollectorOpts) *MongodbCollector {
return exporter
}

// getSession returns the cached *mgo.Session or creates a new session and returns it.
// Use sync.Mutex to avoid race condition around session creation.
func (exporter *MongodbCollector) getSession() *mgo.Session {
exporter.mongoSessLock.Lock()
defer exporter.mongoSessLock.Unlock()

if exporter.mongoSess != nil {
return exporter.mongoSess.Copy()
}
exporter.mongoSess = shared.MongoSession(exporter.Opts.toSessionOps())
if exporter.mongoSess != nil {
return exporter.mongoSess.Copy()
}
return nil
}

// Close cleanly closes the mongo session if it exists.
func (exporter *MongodbCollector) Close() {
if exporter.mongoSess != nil {
exporter.mongoSessLock.Lock()
exporter.mongoSess.Close()
exporter.mongoSessLock.Unlock()
}
}

// Describe sends the super-set of all possible descriptors of metrics collected by this Collector
// to the provided channel and returns once the last descriptor has been sent.
// Part of prometheus.Collector interface.
Expand Down Expand Up @@ -139,9 +168,9 @@ func (exporter *MongodbCollector) scrape(ch chan<- prometheus.Metric) {
}
}(time.Now())

mongoSess := shared.MongoSession(exporter.Opts.toSessionOps())
mongoSess := exporter.getSession()
if mongoSess == nil {
err = errors.New("can't create mongo session")
log.Errorf("can't create mongo session to %s", exporter.Opts.URI)
return
}
defer mongoSess.Close()
Expand Down
10 changes: 7 additions & 3 deletions mongodb_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ var (
" \tIf not provided: System default CAs are used.")
tlsDisableHostnameValidationF = flag.Bool("mongodb.tls-disable-hostname-validation", false, "Do hostname validation for server connection.")

dbPoolLimit = flag.Int("mongodb.max-connections", 1, "Max number of pooled connections to the database.")

// FIXME currently ignored
enabledGroupsFlag = flag.String("groups.enabled", "asserts,durability,background_flushing,connections,extra_info,global_lock,index_counters,network,op_counters,op_counters_repl,memory,locks,metrics", "Comma-separated list of groups to use, for more info see: docs.mongodb.org/manual/reference/command/serverStatus/")
)
Expand Down Expand Up @@ -154,8 +156,8 @@ func startWebServer() {
}

handler := prometheusHandler()

registerCollector()
collector := registerCollector()
defer collector.Close()

if (*sslCertFileF == "") != (*sslKeyFileF == "") {
log.Fatal("One of the flags -web.ssl-cert-file or -web.ssl-key-file is missing to enable HTTPS/TLS")
Expand Down Expand Up @@ -210,16 +212,18 @@ func startWebServer() {
}
}

func registerCollector() {
func registerCollector() *collector.MongodbCollector {
mongodbCollector := collector.NewMongodbCollector(collector.MongodbCollectorOpts{
URI: *uriF,
TLSConnection: *tlsF,
TLSCertificateFile: *tlsCertF,
TLSPrivateKeyFile: *tlsPrivateKeyF,
TLSCaFile: *tlsCAF,
TLSHostnameValidation: !(*tlsDisableHostnameValidationF),
DbPoolLimit: *dbPoolLimit,
})
prometheus.MustRegister(mongodbCollector)
return mongodbCollector
}

func main() {
Expand Down
5 changes: 4 additions & 1 deletion shared/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type MongoSessionOpts struct {
TLSPrivateKeyFile string
TLSCaFile string
TLSHostnameValidation bool
PoolLimit int
}

func MongoSession(opts MongoSessionOpts) *mgo.Session {
Expand All @@ -76,8 +77,10 @@ func MongoSession(opts MongoSessionOpts) *mgo.Session {
return nil
}
session.SetMode(mgo.Eventual, true)
session.SetPoolLimit(opts.PoolLimit)
session.SetPrefetch(0.00)
session.SetSyncTimeout(syncMongodbTimeout)
session.SetSocketTimeout(0)
session.SetSocketTimeout(dialMongodbTimeout)
return session
}

Expand Down