Skip to content

Commit

Permalink
replSetGetConfigCollector (#295)
Browse files Browse the repository at this point in the history
* replSetGetConfigCollector. it just works

* Use member_idx for host field to match with replStatus

* Add --disable.replicasetconfig option flag

* Use errors.As() instead of type assertion.

* Add replset_config_collector_test.

* Revert "Use errors.As() instead of type assertion."

This reverts commit 54ded8c.

* nolint - errors.As seems to cause segfault.

* Update exporter/replset_config_collector.go

* Update exporter/replset_config_collector_test.go

* Update exporter/replset_config_collector.go

* Update exporter/replset_config_collector_test.go

* Update exporter/replset_config_collector.go

* Update exporter/exporter.go

* Update exporter/exporter.go

* Update exporter/replset_config_collector_test.go

* Update replset_config_collector.go

* Update replset_config_collector_test.go

* Update exporter/replset_config_collector_test.go

* Update exporter/replset_config_collector.go

* Update replset_config_collector.go

---------

Co-authored-by: JiriCtvrtka <[email protected]>
Co-authored-by: Denys Kondratenko <[email protected]>
Co-authored-by: Nurlan Moldomurov <[email protected]>
Co-authored-by: Nurlan Moldomurov <[email protected]>
  • Loading branch information
5 people authored Nov 11, 2024
1 parent 70ab813 commit 0239f77
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Currently, these metric sources are implemented:
- $indexStats
- getDiagnosticData
- replSetGetStatus
- replSetGetConfig
- serverStatus

## Supported MongoDB versions
Expand Down
10 changes: 10 additions & 0 deletions exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type Opts struct {
EnableDBStatsFreeStorage bool
EnableDiagnosticData bool
EnableReplicasetStatus bool
EnableReplicasetConfig bool
EnableCurrentopMetrics bool
EnableTopMetrics bool
EnableIndexStats bool
Expand Down Expand Up @@ -163,6 +164,7 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol
e.opts.EnableCollStats = true
e.opts.EnableTopMetrics = true
e.opts.EnableReplicasetStatus = true
e.opts.EnableReplicasetConfig = true
e.opts.EnableIndexStats = true
e.opts.EnableCurrentopMetrics = true
e.opts.EnableProfile = true
Expand Down Expand Up @@ -239,6 +241,12 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol
registry.MustRegister(rsgsc)
}

// replSetGetStatus is not supported through mongos.
if e.opts.EnableReplicasetConfig && nodeType != typeMongos && requestOpts.EnableReplicasetConfig {
rsgsc := newReplicationSetConfigCollector(ctx, client, e.opts.Logger,
e.opts.CompatibleMode, topologyInfo)
registry.MustRegister(rsgsc)
}
if e.opts.EnableShards && nodeType == typeMongos && requestOpts.EnableShards {
sc := newShardsCollector(ctx, client, e.opts.Logger, e.opts.CompatibleMode)
registry.MustRegister(sc)
Expand Down Expand Up @@ -374,6 +382,8 @@ func GetRequestOpts(filters []string, defaultOpts *Opts) Opts {
requestOpts.EnableDiagnosticData = true
case "replicasetstatus":
requestOpts.EnableReplicasetStatus = true
case "replicasetconfig":
requestOpts.EnableReplicasetConfig = true
case "dbstats":
requestOpts.EnableDBStats = true
case "topmetrics":
Expand Down
3 changes: 3 additions & 0 deletions exporter/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,9 @@ func processSlice(prefix, k string, v []interface{}, commonLabels map[string]str
if state, ok := s["stateStr"].(string); ok {
labels["member_state"] = state
}
if host, ok := s["host"].(string); ok {
labels["member_idx"] = host
}

metrics = append(metrics, makeMetrics(prefix+k, s, labels, compatibleMode)...)
}
Expand Down
94 changes: 94 additions & 0 deletions exporter/replset_config_collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// mongodb_exporter
// Copyright (C) 2017 Percona LLC
//
// 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 exporter

import (
"context"

"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/sirupsen/logrus"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
)

type replSetGetConfigCollector struct {
ctx context.Context
base *baseCollector

compatibleMode bool
topologyInfo labelsGetter
}

// newReplicationSetConfigCollector creates a collector for configuration of replication set.
func newReplicationSetConfigCollector(ctx context.Context, client *mongo.Client, logger *logrus.Logger, compatible bool, topology labelsGetter) *replSetGetConfigCollector {
return &replSetGetConfigCollector{
ctx: ctx,
base: newBaseCollector(client, logger.WithFields(logrus.Fields{"collector": "replset_config"})),

compatibleMode: compatible,
topologyInfo: topology,
}
}

func (d *replSetGetConfigCollector) Describe(ch chan<- *prometheus.Desc) {
d.base.Describe(d.ctx, ch, d.collect)
}

func (d *replSetGetConfigCollector) Collect(ch chan<- prometheus.Metric) {
d.base.Collect(ch)
}

func (d *replSetGetConfigCollector) collect(ch chan<- prometheus.Metric) {
defer measureCollectTime(ch, "mongodb", "replset_config")()

logger := d.base.logger
client := d.base.client

cmd := bson.D{{Key: "replSetGetConfig", Value: "1"}}
res := client.Database("admin").RunCommand(d.ctx, cmd)

var m bson.M

if err := res.Decode(&m); err != nil {
if e, ok := err.(mongo.CommandError); ok { //nolint // https://github.com/percona/mongodb_exporter/pull/295#issuecomment-922874632
if e.Code == replicationNotYetInitialized || e.Code == replicationNotEnabled {
return
}
}
logger.Errorf("cannot get replSetGetConfig: %s", err)

return
}

config, ok := m["config"].(bson.M)
if !ok {
err := errors.Wrapf(errUnexpectedDataType, "%T for data field", m["config"])
logger.Errorf("cannot decode getDiagnosticData: %s", err)

return
}
m = config

logger.Debug("replSetGetConfig result:")
debugResult(logger, m)

for _, metric := range makeMetrics("rs_cfg", m, d.topologyInfo.baseLabels(), d.compatibleMode) {
ch <- metric
}
}

var _ prometheus.Collector = (*replSetGetConfigCollector)(nil)
72 changes: 72 additions & 0 deletions exporter/replset_config_collector_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// mongodb_exporter
// Copyright (C) 2017 Percona LLC
//
// 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 exporter

import (
"context"
"strings"
"testing"
"time"

"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"

"github.com/percona/mongodb_exporter/internal/tu"
)

func TestReplsetConfigCollector(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()

client := tu.DefaultTestClient(ctx, t)

ti := labelsGetterMock{}

c := newReplicationSetConfigCollector(ctx, client, logrus.New(), false, ti)

// The last \n at the end of this string is important
expected := strings.NewReader(`
# HELP mongodb_rs_cfg_protocolVersion rs_cfg.
# TYPE mongodb_rs_cfg_protocolVersion untyped
mongodb_rs_cfg_protocolVersion 1` + "\n")
// Filter metrics for 2 reasons:
// 1. The result is huge
// 2. We need to check against know values. Don't use metrics that return counters like uptime
// or counters like the number of transactions because they won't return a known value to compare
filter := []string{
"mongodb_rs_cfg_protocolVersion",
}
err := testutil.CollectAndCompare(c, expected, filter...)
assert.NoError(t, err)
}

func TestReplsetConfigCollectorNoSharding(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()

client := tu.TestClient(ctx, tu.MongoDBStandAlonePort, t)

ti := labelsGetterMock{}

c := newReplicationSetConfigCollector(ctx, client, logrus.New(), false, ti)

// Replication set metrics should not be generated for unsharded server
count := testutil.CollectAndCount(c)

metaMetricCount := 1
assert.Equal(t, metaMetricCount, count, "Mismatch in metric count for collector run on unsharded server")
}
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type GlobalFlags struct {
EnableExporterMetrics bool `name:"collector.exporter-metrics" help:"Enable collecting metrics about the exporter itself (process_*, go_*)" negatable:"" default:"True"`
EnableDiagnosticData bool `name:"collector.diagnosticdata" help:"Enable collecting metrics from getDiagnosticData"`
EnableReplicasetStatus bool `name:"collector.replicasetstatus" help:"Enable collecting metrics from replSetGetStatus"`
EnableReplicasetConfig bool `name:"collector.replicasetconfig" help:"Enable collecting metrics from replSetGetConfig"`
EnableDBStats bool `name:"collector.dbstats" help:"Enable collecting metrics from dbStats"`
EnableDBStatsFreeStorage bool `name:"collector.dbstatsfreestorage" help:"Enable collecting free space metrics from dbStats"`
EnableTopMetrics bool `name:"collector.topmetrics" help:"Enable collecting metrics from top admin command"`
Expand Down Expand Up @@ -166,6 +167,7 @@ func buildExporter(opts GlobalFlags, uri string, log *logrus.Logger) *exporter.E
DisableDefaultRegistry: !opts.EnableExporterMetrics,
EnableDiagnosticData: opts.EnableDiagnosticData,
EnableReplicasetStatus: opts.EnableReplicasetStatus,
EnableReplicasetConfig: opts.EnableReplicasetConfig,
EnableCurrentopMetrics: opts.EnableCurrentopMetrics,
EnableTopMetrics: opts.EnableTopMetrics,
EnableDBStats: opts.EnableDBStats,
Expand Down
1 change: 1 addition & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ func TestBuildExporter(t *testing.T) {

EnableDiagnosticData: true,
EnableReplicasetStatus: true,
EnableReplicasetConfig: true,

CompatibleMode: true,
}
Expand Down

0 comments on commit 0239f77

Please sign in to comment.