From 66ac77f67c7781dea0b1b17100301cef209e2476 Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Tue, 29 Oct 2024 00:31:04 +0300 Subject: [PATCH 01/15] PMM-13477 Support MongoDB 8.0 --- exporter/diagnostic_data_collector.go | 3 +++ exporter/metrics.go | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/exporter/diagnostic_data_collector.go b/exporter/diagnostic_data_collector.go index 3fb322bdc..64413e501 100644 --- a/exporter/diagnostic_data_collector.go +++ b/exporter/diagnostic_data_collector.go @@ -108,6 +108,9 @@ func (d *diagnosticDataCollector) collect(ch chan<- prometheus.Metric) { err = errors.Wrapf(errUnexpectedDataType, "%T for data field", m["data"]) logger.Errorf("cannot decode getDiagnosticData: %s", err) } + if c, ok := m["common"].(bson.M); ok { + m = c + } logger.Debug("getDiagnosticData result") debugResult(logger, m) diff --git a/exporter/metrics.go b/exporter/metrics.go index ef06903b7..b7615cc61 100644 --- a/exporter/metrics.go +++ b/exporter/metrics.go @@ -16,6 +16,7 @@ package exporter import ( + "fmt" "regexp" "strings" "time" @@ -186,6 +187,15 @@ func makeRawMetric(prefix, name string, value interface{}, labels map[string]str rm.lv = append(rm.lv, name) } + if name == "start" || name == "end" { + fmt.Println("fqName: ", fqName) + fmt.Println("help: ", help) + fmt.Println("val: ", *f) + fmt.Println("vt: ", metricType) + fmt.Println("ln: ", rm.ln) + fmt.Println("lv: ", rm.lv) + } + return rm, nil } From 959dc79c3cd1b2846dc7d3062404e10210c1ee2f Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Tue, 29 Oct 2024 14:45:00 +0300 Subject: [PATCH 02/15] PMM-13477 Support MongoDB 8.0 --- exporter/exporter.go | 22 ++++++++++++++++++++-- exporter/exporter_test.go | 3 +-- exporter/metrics.go | 10 ---------- exporter/replset_status_collector.go | 15 +++++++++++++-- exporter/replset_status_collector_test.go | 4 ++-- exporter/secondary_lag_test.go | 3 ++- exporter/topology_info.go | 8 +++++--- exporter/topology_info_test.go | 3 ++- exporter/v1_compatibility.go | 16 ++++++++-------- 9 files changed, 53 insertions(+), 31 deletions(-) diff --git a/exporter/exporter.go b/exporter/exporter.go index 75a166061..af2beb7eb 100644 --- a/exporter/exporter.go +++ b/exporter/exporter.go @@ -19,6 +19,7 @@ package exporter import ( "context" "fmt" + "go.mongodb.org/mongo-driver/bson" "net/http" _ "net/http/pprof" "strconv" @@ -129,12 +130,29 @@ func (e *Exporter) getTotalCollectionsCount() int { return e.totalCollectionsCount } +type MongoVersion struct { + VersionString string `bson:"version"` + PSMDBVersion string `bson:"psmdbVersion"` + Version []int `bson:"versionArray"` +} + +func getMongoVersion(ctx context.Context, client *mongo.Client) (*MongoVersion, error) { + var ver MongoVersion + err := client.Database("admin").RunCommand(ctx, bson.D{{"buildInfo", 1}}).Decode(&ver) + return &ver, err +} + func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topologyInfo labelsGetter, requestOpts Opts) *prometheus.Registry { registry := prometheus.NewRegistry() nodeType, err := getNodeType(ctx, client) if err != nil { - e.logger.Errorf("Registry - Cannot get node type to check if this is a mongos : %s", err) + e.logger.Errorf("Registry - Cannot get node type : %s", err) + } + + version, err := getMongoVersion(ctx, client) + if err != nil { + e.logger.Warnf("Registry - Cannot get MongoDB version: %s", err) } gc := newGeneralCollector(ctx, client, nodeType, e.opts.Logger) @@ -234,7 +252,7 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol // replSetGetStatus is not supported through mongos. if e.opts.EnableReplicasetStatus && nodeType != typeMongos && requestOpts.EnableReplicasetStatus { rsgsc := newReplicationSetStatusCollector(ctx, client, e.opts.Logger, - e.opts.CompatibleMode, topologyInfo) + e.opts.CompatibleMode, topologyInfo, version) registry.MustRegister(rsgsc) } diff --git a/exporter/exporter_test.go b/exporter/exporter_test.go index 8f59c48e2..8ea89851e 100644 --- a/exporter/exporter_test.go +++ b/exporter/exporter_test.go @@ -187,8 +187,7 @@ func TestMongoS(t *testing.T) { e := New(exporterOpts) - rsgsc := newReplicationSetStatusCollector(ctx, client, e.opts.Logger, - e.opts.CompatibleMode, new(labelsGetterMock)) + rsgsc := newReplicationSetStatusCollector(ctx, client, e.opts.Logger, e.opts.CompatibleMode, new(labelsGetterMock), nil) r := e.makeRegistry(ctx, client, new(labelsGetterMock), *e.opts) diff --git a/exporter/metrics.go b/exporter/metrics.go index b7615cc61..ef06903b7 100644 --- a/exporter/metrics.go +++ b/exporter/metrics.go @@ -16,7 +16,6 @@ package exporter import ( - "fmt" "regexp" "strings" "time" @@ -187,15 +186,6 @@ func makeRawMetric(prefix, name string, value interface{}, labels map[string]str rm.lv = append(rm.lv, name) } - if name == "start" || name == "end" { - fmt.Println("fqName: ", fqName) - fmt.Println("help: ", help) - fmt.Println("val: ", *f) - fmt.Println("vt: ", metricType) - fmt.Println("ln: ", rm.ln) - fmt.Println("lv: ", rm.lv) - } - return rm, nil } diff --git a/exporter/replset_status_collector.go b/exporter/replset_status_collector.go index afbbd6d39..77807770c 100644 --- a/exporter/replset_status_collector.go +++ b/exporter/replset_status_collector.go @@ -17,6 +17,7 @@ package exporter import ( "context" + "strings" "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" @@ -35,16 +36,19 @@ type replSetGetStatusCollector struct { compatibleMode bool topologyInfo labelsGetter + + version *MongoVersion } // newReplicationSetStatusCollector creates a collector for statistics on replication set. -func newReplicationSetStatusCollector(ctx context.Context, client *mongo.Client, logger *logrus.Logger, compatible bool, topology labelsGetter) *replSetGetStatusCollector { +func newReplicationSetStatusCollector(ctx context.Context, client *mongo.Client, logger *logrus.Logger, compatible bool, topology labelsGetter, version *MongoVersion) *replSetGetStatusCollector { return &replSetGetStatusCollector{ ctx: ctx, base: newBaseCollector(client, logger.WithFields(logrus.Fields{"collector": "replset_status"})), compatibleMode: compatible, topologyInfo: topology, + version: version, } } @@ -81,9 +85,16 @@ func (d *replSetGetStatusCollector) collect(ch chan<- prometheus.Metric) { logger.Debug("replSetGetStatus result:") debugResult(logger, m) - for _, metric := range makeMetrics("", m, d.topologyInfo.baseLabels(), d.compatibleMode) { + for _, metric := range makeMetrics("replset", m, d.topologyInfo.baseLabels(), d.compatibleMode) { ch <- metric } + if d.compatibleMode && strings.HasPrefix(d.version.VersionString, "8.") { + logger.Infof("collecting compatibility metrics for version %s", d.version.VersionString) + metrics := replSetMetrics(m, logger) + for _, metric := range metrics { + ch <- metric + } + } } var _ prometheus.Collector = (*replSetGetStatusCollector)(nil) diff --git a/exporter/replset_status_collector_test.go b/exporter/replset_status_collector_test.go index 12a5cbcb0..8d62f7ae7 100644 --- a/exporter/replset_status_collector_test.go +++ b/exporter/replset_status_collector_test.go @@ -36,7 +36,7 @@ func TestReplsetStatusCollector(t *testing.T) { ti := labelsGetterMock{} - c := newReplicationSetStatusCollector(ctx, client, logrus.New(), false, ti) + c := newReplicationSetStatusCollector(ctx, client, logrus.New(), false, ti, nil) // The last \n at the end of this string is important expected := strings.NewReader(` @@ -66,7 +66,7 @@ func TestReplsetStatusCollectorNoSharding(t *testing.T) { ti := labelsGetterMock{} - c := newReplicationSetStatusCollector(ctx, client, logrus.New(), false, ti) + c := newReplicationSetStatusCollector(ctx, client, logrus.New(), false, ti, nil) // Replication set metrics should not be generated for unsharded server count := testutil.CollectAndCount(c) diff --git a/exporter/secondary_lag_test.go b/exporter/secondary_lag_test.go index ce246f09b..72ac2bc7b 100644 --- a/exporter/secondary_lag_test.go +++ b/exporter/secondary_lag_test.go @@ -17,6 +17,7 @@ package exporter import ( "context" + "github.com/sirupsen/logrus" "strings" "testing" "time" @@ -126,7 +127,7 @@ func TestSecondaryLag(t *testing.T) { assert.NoError(t, err) m, _ = m["data"].(bson.M) - metrics := replSetMetrics(m) + metrics := replSetMetrics(m, logrus.WithField("component", "test")) var lag prometheus.Metric for _, m := range metrics { if strings.HasPrefix(m.Desc().String(), `Desc{fqName: "mongodb_mongod_replset_member_replication_lag"`) { diff --git a/exporter/topology_info.go b/exporter/topology_info.go index 08d05d841..254f8795e 100644 --- a/exporter/topology_info.go +++ b/exporter/topology_info.go @@ -103,7 +103,7 @@ func (t *topologyInfo) loadLabels(ctx context.Context) error { t.labels = make(map[string]string) - role, err := getClusterRole(ctx, t.client) + role, err := getClusterRole(ctx, t.client, t.logger) if err != nil { return errors.Wrap(err, "cannot get node type for topology info") } @@ -157,13 +157,12 @@ func getNodeType(ctx context.Context, client *mongo.Client) (mongoDBNodeType, er return typeMongod, nil } -func getClusterRole(ctx context.Context, client *mongo.Client) (string, error) { +func getClusterRole(ctx context.Context, client *mongo.Client, logger *logrus.Entry) (string, error) { cmdOpts := primitive.M{} // Not always we can get this info. For example, we cannot get this for hidden hosts so // if there is an error, just ignore it res := client.Database("admin").RunCommand(ctx, primitive.D{ {Key: "getCmdLineOpts", Value: 1}, - {Key: "recordStats", Value: 1}, }) if res.Err() != nil { @@ -174,6 +173,9 @@ func getClusterRole(ctx context.Context, client *mongo.Client) (string, error) { return "", errors.Wrap(err, "cannot decode getCmdLineOpts response") } + logger.Debug("getCmdLineOpts response:") + debugResult(logger, cmdOpts) + if walkTo(cmdOpts, []string{"parsed", "sharding", "configDB"}) != nil { return "mongos", nil } diff --git a/exporter/topology_info_test.go b/exporter/topology_info_test.go index d1e4f364f..7d7d3ec1a 100644 --- a/exporter/topology_info_test.go +++ b/exporter/topology_info_test.go @@ -136,7 +136,8 @@ func TestGetClusterRole(t *testing.T) { require.NoError(t, err) client := tu.TestClient(ctx, port, t) - nodeType, err := getClusterRole(ctx, client) + logger := logrus.WithField("component", "test") + nodeType, err := getClusterRole(ctx, client, logger) assert.NoError(t, err) assert.Equal(t, tc.want, nodeType, fmt.Sprintf("container name: %s, port: %s", tc.containerName, port)) } diff --git a/exporter/v1_compatibility.go b/exporter/v1_compatibility.go index 3a3d49895..04db03d12 100644 --- a/exporter/v1_compatibility.go +++ b/exporter/v1_compatibility.go @@ -812,8 +812,10 @@ func specialMetrics(ctx context.Context, client *mongo.Client, m bson.M, nodeTyp if nodeType != typeArbiter { metrics = append(metrics, myState(ctx, client)) - if rm := replSetMetrics(m); rm != nil { - metrics = append(metrics, rm...) + if replSetGetStatus, ok := m["replSetGetStatus"].(bson.M); ok { + if rm := replSetMetrics(replSetGetStatus, l); rm != nil { + metrics = append(metrics, rm...) + } } if nodeType != typeMongos { @@ -982,17 +984,15 @@ func oplogStatus(ctx context.Context, client *mongo.Client) ([]prometheus.Metric return []prometheus.Metric{headMetric, tailMetric}, nil } -func replSetMetrics(d bson.M) []prometheus.Metric { - replSetGetStatus, ok := d["replSetGetStatus"].(bson.M) - if !ok { - return nil - } +func replSetMetrics(d bson.M, l *logrus.Entry) []prometheus.Metric { var repl proto.ReplicaSetStatus - b, err := bson.Marshal(replSetGetStatus) + b, err := bson.Marshal(d) if err != nil { + l.Warnf("cannot marshal replica set status: %s", err) return nil } if err := bson.Unmarshal(b, &repl); err != nil { + l.Warnf("cannot unmarshal replica set status: %s", err) return nil } From b87ecd66a0c73de8921b89d2b64ec0eaa26e9efb Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Thu, 31 Oct 2024 10:11:58 +0300 Subject: [PATCH 03/15] PMM-13477 Support MongoDB 8.0 --- exporter/metrics.go | 2 ++ exporter/replset_status_collector.go | 16 +++++++++++----- exporter/v1_compatibility.go | 21 +++++++++++++++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/exporter/metrics.go b/exporter/metrics.go index ef06903b7..a6316e461 100644 --- a/exporter/metrics.go +++ b/exporter/metrics.go @@ -52,6 +52,7 @@ var ( prefixes = [][]string{ {"serverStatus.wiredTiger.transaction", "ss_wt_txn"}, {"serverStatus.wiredTiger", "ss_wt"}, + {"serverStatus.queues.execution", "ss_wt_concurrentTransactions"}, {"serverStatus", "ss"}, {"replSetGetStatus", "rs"}, {"systemMetrics", "sys"}, @@ -105,6 +106,7 @@ var ( "serverStatus.opcountersRepl.": "legacy_op_type", "serverStatus.transactions.commitTypes.": "commit_type", "serverStatus.wiredTiger.concurrentTransactions.": "txn_rw_type", + "serverStatus.queues.execution.": "txn_rw_type", "serverStatus.wiredTiger.perf.": "perf_bucket", "systemMetrics.disks.": "device_name", } diff --git a/exporter/replset_status_collector.go b/exporter/replset_status_collector.go index 77807770c..58bd5f2dd 100644 --- a/exporter/replset_status_collector.go +++ b/exporter/replset_status_collector.go @@ -85,15 +85,21 @@ func (d *replSetGetStatusCollector) collect(ch chan<- prometheus.Metric) { logger.Debug("replSetGetStatus result:") debugResult(logger, m) - for _, metric := range makeMetrics("replset", m, d.topologyInfo.baseLabels(), d.compatibleMode) { + for _, metric := range makeMetrics("", m, d.topologyInfo.baseLabels(), d.compatibleMode) { ch <- metric } - if d.compatibleMode && strings.HasPrefix(d.version.VersionString, "8.") { - logger.Infof("collecting compatibility metrics for version %s", d.version.VersionString) - metrics := replSetMetrics(m, logger) - for _, metric := range metrics { + + if strings.HasPrefix(d.version.VersionString, "8.") { + for _, metric := range makeMetrics("rs", m, d.topologyInfo.baseLabels(), d.compatibleMode) { ch <- metric } + if d.compatibleMode { + logger.Infof("collecting compatibility metrics for version %s", d.version.VersionString) + metrics := replSetMetrics(m, logger) + for _, metric := range metrics { + ch <- metric + } + } } } diff --git a/exporter/v1_compatibility.go b/exporter/v1_compatibility.go index 04db03d12..9dd56c8e3 100644 --- a/exporter/v1_compatibility.go +++ b/exporter/v1_compatibility.go @@ -473,10 +473,22 @@ func conversions() []conversion { oldName: "mongodb_mongod_wiredtiger_transactions_checkpoint_milliseconds_total", newName: "mongodb_ss_wt_txn_transaction_checkpoint_total_time_msecs", }, + { + oldName: "mongodb_mongod_wiredtiger_transactions_checkpoint_milliseconds_total", + newName: "mongodb_ss_wt_checkpoint_total_time_msecs", + }, { oldName: "mongodb_mongod_wiredtiger_transactions_running_checkpoints", newName: "mongodb_ss_wt_txn_transaction_checkpoint_currently_running", }, + { + oldName: "mongodb_mongod_wiredtiger_transactions_running_checkpoints", + newName: "mongodb_ss_wt_checkpoint_currently_running", + }, + { + oldName: "mongodb_ss_tcmalloc_tcmalloc_thread_cache_free_bytes", + newName: "mongodb_ss_tcmalloc_tcmalloc_thread_cache_free", + }, { oldName: "mongodb_mongod_wiredtiger_transactions_total", prefix: "mongodb_ss_wt_txn_transactions", @@ -577,6 +589,15 @@ func conversions() []conversion { "max_time_msecs": "max", }, }, + { + oldName: "mongodb_mongod_wiredtiger_transactions_checkpoint_milliseconds", + prefix: "mongodb_ss_wt_checkpoint", + suffixLabel: "type", + suffixMapping: map[string]string{ + "min_time_msecs": "min", + "max_time_msecs": "max", + }, + }, { oldName: "mongodb_mongod_global_lock_current_queue", prefix: "mongodb_mongod_global_lock_current_queue", From 52a07d514abb5cc14bbd96921d04449bab6c2a4a Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Sat, 2 Nov 2024 12:05:57 +0300 Subject: [PATCH 04/15] PMM-13477 Fix tests. --- exporter/exporter_test.go | 5 ++++- exporter/replset_status_collector_test.go | 12 +++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/exporter/exporter_test.go b/exporter/exporter_test.go index 8ea89851e..ecfe36f47 100644 --- a/exporter/exporter_test.go +++ b/exporter/exporter_test.go @@ -18,6 +18,7 @@ package exporter import ( "context" "fmt" + "github.com/stretchr/testify/require" "io" "net" "net/http" @@ -187,7 +188,9 @@ func TestMongoS(t *testing.T) { e := New(exporterOpts) - rsgsc := newReplicationSetStatusCollector(ctx, client, e.opts.Logger, e.opts.CompatibleMode, new(labelsGetterMock), nil) + version, err := getMongoVersion(ctx, client) + require.NoError(t, err) + rsgsc := newReplicationSetStatusCollector(ctx, client, e.opts.Logger, e.opts.CompatibleMode, new(labelsGetterMock), version) r := e.makeRegistry(ctx, client, new(labelsGetterMock), *e.opts) diff --git a/exporter/replset_status_collector_test.go b/exporter/replset_status_collector_test.go index 8d62f7ae7..3e412a60e 100644 --- a/exporter/replset_status_collector_test.go +++ b/exporter/replset_status_collector_test.go @@ -17,6 +17,7 @@ package exporter import ( "context" + "github.com/stretchr/testify/require" "strings" "testing" "time" @@ -36,7 +37,9 @@ func TestReplsetStatusCollector(t *testing.T) { ti := labelsGetterMock{} - c := newReplicationSetStatusCollector(ctx, client, logrus.New(), false, ti, nil) + version, err := getMongoVersion(ctx, client) + require.NoError(t, err) + c := newReplicationSetStatusCollector(ctx, client, logrus.New(), false, ti, version) // The last \n at the end of this string is important expected := strings.NewReader(` @@ -54,7 +57,7 @@ func TestReplsetStatusCollector(t *testing.T) { "mongodb_myState", "mongodb_ok", } - err := testutil.CollectAndCompare(c, expected, filter...) + err = testutil.CollectAndCompare(c, expected, filter...) assert.NoError(t, err) } @@ -66,7 +69,10 @@ func TestReplsetStatusCollectorNoSharding(t *testing.T) { ti := labelsGetterMock{} - c := newReplicationSetStatusCollector(ctx, client, logrus.New(), false, ti, nil) + version, err := getMongoVersion(ctx, client) + require.NoError(t, err) + + c := newReplicationSetStatusCollector(ctx, client, logrus.New(), false, ti, version) // Replication set metrics should not be generated for unsharded server count := testutil.CollectAndCount(c) From a2a63ce714b17a3d9670e5838eae36bb1dcc55aa Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Sat, 2 Nov 2024 12:10:41 +0300 Subject: [PATCH 05/15] PMM-13477 Fix imports and test matrix. --- .github/workflows/go.yml | 7 +++++++ exporter/exporter.go | 2 +- exporter/exporter_test.go | 2 +- exporter/replset_status_collector_test.go | 2 +- exporter/secondary_lag_test.go | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index d057866e9..43ca4b205 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -22,8 +22,15 @@ jobs: image: - mongo:4.4 - mongo:5.0 + - mongo:6.0 + - mongo:7.0 + - mongo:8.0 + - mongo:latest - percona/percona-server-mongodb:4.4 - percona/percona-server-mongodb:5.0 + - percona/percona-server-mongodb:6.0 + - percona/percona-server-mongodb:7.0 + - percona/percona-server-mongodb:latest runs-on: ubuntu-latest diff --git a/exporter/exporter.go b/exporter/exporter.go index af2beb7eb..98e62b4db 100644 --- a/exporter/exporter.go +++ b/exporter/exporter.go @@ -19,7 +19,6 @@ package exporter import ( "context" "fmt" - "go.mongodb.org/mongo-driver/bson" "net/http" _ "net/http/pprof" "strconv" @@ -29,6 +28,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/sirupsen/logrus" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "github.com/percona/mongodb_exporter/exporter/dsn_fix" diff --git a/exporter/exporter_test.go b/exporter/exporter_test.go index ecfe36f47..0b1a6db0d 100644 --- a/exporter/exporter_test.go +++ b/exporter/exporter_test.go @@ -18,7 +18,6 @@ package exporter import ( "context" "fmt" - "github.com/stretchr/testify/require" "io" "net" "net/http" @@ -31,6 +30,7 @@ import ( "github.com/prometheus/client_golang/prometheus/testutil" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/percona/mongodb_exporter/internal/tu" ) diff --git a/exporter/replset_status_collector_test.go b/exporter/replset_status_collector_test.go index 3e412a60e..c25da16ca 100644 --- a/exporter/replset_status_collector_test.go +++ b/exporter/replset_status_collector_test.go @@ -17,7 +17,6 @@ package exporter import ( "context" - "github.com/stretchr/testify/require" "strings" "testing" "time" @@ -25,6 +24,7 @@ import ( "github.com/prometheus/client_golang/prometheus/testutil" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/percona/mongodb_exporter/internal/tu" ) diff --git a/exporter/secondary_lag_test.go b/exporter/secondary_lag_test.go index 72ac2bc7b..bf12761ce 100644 --- a/exporter/secondary_lag_test.go +++ b/exporter/secondary_lag_test.go @@ -17,13 +17,13 @@ package exporter import ( "context" - "github.com/sirupsen/logrus" "strings" "testing" "time" "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" + "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/bson" From a727a92ee8486215b1687a754b06e65e9d118be7 Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Sat, 2 Nov 2024 15:38:33 +0300 Subject: [PATCH 06/15] PMM-13477 A little bit of refactoring. --- exporter/diagnostic_data_collector.go | 13 +++---- exporter/diagnostic_data_collector_test.go | 43 ++++++++++++++++------ exporter/encryption_info_test.go | 8 +++- exporter/exporter.go | 21 ++--------- exporter/exporter_test.go | 2 +- exporter/replset_status_collector.go | 8 ++-- exporter/replset_status_collector_test.go | 4 +- 7 files changed, 55 insertions(+), 44 deletions(-) diff --git a/exporter/diagnostic_data_collector.go b/exporter/diagnostic_data_collector.go index 64413e501..49d19f197 100644 --- a/exporter/diagnostic_data_collector.go +++ b/exporter/diagnostic_data_collector.go @@ -35,12 +35,14 @@ type diagnosticDataCollector struct { ctx context.Context base *baseCollector + buildInfo buildInfo + compatibleMode bool topologyInfo labelsGetter } // newDiagnosticDataCollector creates a collector for diagnostic information. -func newDiagnosticDataCollector(ctx context.Context, client *mongo.Client, logger *logrus.Logger, compatible bool, topology labelsGetter) *diagnosticDataCollector { +func newDiagnosticDataCollector(ctx context.Context, client *mongo.Client, logger *logrus.Logger, compatible bool, topology labelsGetter, buildInfo buildInfo) *diagnosticDataCollector { nodeType, err := getNodeType(ctx, client) if err != nil { logger.WithFields(logrus.Fields{ @@ -57,6 +59,8 @@ func newDiagnosticDataCollector(ctx context.Context, client *mongo.Client, logge ctx: ctx, base: newBaseCollector(client, logger.WithFields(logrus.Fields{"collector": "diagnostic_data"})), + buildInfo: buildInfo, + compatibleMode: compatible, topologyInfo: topology, } @@ -135,12 +139,7 @@ func (d *diagnosticDataCollector) collect(ch chan<- prometheus.Metric) { } if d.compatibleMode { - buildInfo, err := retrieveMongoDBBuildInfo(d.ctx, client, logger) - if err != nil { - logger.Errorf("cannot retrieve MongoDB buildInfo: %s", err) - } - - metrics = append(metrics, serverVersion(buildInfo)) + metrics = append(metrics, serverVersion(d.buildInfo)) if nodeType == typeArbiter { if hm := arbiterMetrics(d.ctx, client, logger); hm != nil { diff --git a/exporter/diagnostic_data_collector_test.go b/exporter/diagnostic_data_collector_test.go index 264701d6f..d72bb882f 100644 --- a/exporter/diagnostic_data_collector_test.go +++ b/exporter/diagnostic_data_collector_test.go @@ -46,7 +46,10 @@ func TestDiagnosticDataCollector(t *testing.T) { logger := logrus.New() ti := labelsGetterMock{} - c := newDiagnosticDataCollector(ctx, client, logger, false, ti) + dbBuildInfo, err := retrieveMongoDBBuildInfo(ctx, client, logger.WithField("component", "test")) + require.NoError(t, err) + + c := newDiagnosticDataCollector(ctx, client, logger, false, ti, dbBuildInfo) // The last \n at the end of this string is important expected := strings.NewReader(` @@ -70,7 +73,7 @@ func TestDiagnosticDataCollector(t *testing.T) { "mongodb_oplog_stats_wt_transaction_update_conflicts", } - err := testutil.CollectAndCompare(c, expected, filter...) + err = testutil.CollectAndCompare(c, expected, filter...) assert.NoError(t, err) } @@ -188,7 +191,10 @@ func TestCollectorWithCompatibleMode(t *testing.T) { logger := logrus.New() ti := labelsGetterMock{} - c := newDiagnosticDataCollector(ctx, client, logger, true, ti) + dbBuildInfo, err := retrieveMongoDBBuildInfo(ctx, client, logger.WithField("component", "test")) + require.NoError(t, err) + + c := newDiagnosticDataCollector(ctx, client, logger, true, ti, dbBuildInfo) err = testutil.CollectAndCompare(c, tt.expectedMetrics(), tt.metricsFilter...) assert.NoError(t, err) @@ -202,12 +208,16 @@ func TestAllDiagnosticDataCollectorMetrics(t *testing.T) { client := tu.DefaultTestClient(ctx, t) - ti := newTopologyInfo(ctx, client, logrus.New()) + logger := logrus.New() + ti := newTopologyInfo(ctx, client, logger) + + dbBuildInfo, err := retrieveMongoDBBuildInfo(ctx, client, logger.WithField("component", "test")) + require.NoError(t, err) - c := newDiagnosticDataCollector(ctx, client, logrus.New(), true, ti) + c := newDiagnosticDataCollector(ctx, client, logger, true, ti, dbBuildInfo) reg := prometheus.NewRegistry() - err := reg.Register(c) + err = reg.Register(c) require.NoError(t, err) metrics := helpers.CollectMetrics(c) actualMetrics := helpers.ReadMetrics(metrics) @@ -281,7 +291,11 @@ func TestDiagnosticDataErrors(t *testing.T) { logger, hook := logrustest.NewNullLogger() ti := newTopologyInfo(ctx, client, logger) - c := newDiagnosticDataCollector(ctx, client, logger, true, ti) + + dbBuildInfo, err := retrieveMongoDBBuildInfo(ctx, client, logger.WithField("component", "test")) + require.NoError(t, err) + + c := newDiagnosticDataCollector(ctx, client, logger, true, ti, dbBuildInfo) reg := prometheus.NewRegistry() err = reg.Register(c) @@ -318,11 +332,15 @@ func TestContextTimeout(t *testing.T) { client := tu.DefaultTestClient(ctx, t) - ti := newTopologyInfo(ctx, client, logrus.New()) + logger := logrus.New() + ti := newTopologyInfo(ctx, client, logger) + + dbBuildInfo, err := retrieveMongoDBBuildInfo(ctx, client, logger.WithField("component", "test")) + require.NoError(t, err) dbCount := 100 - err := addTestData(ctx, client, dbCount) + err = addTestData(ctx, client, dbCount) assert.NoError(t, err) defer cleanTestData(ctx, client, dbCount) //nolint:errcheck @@ -330,7 +348,7 @@ func TestContextTimeout(t *testing.T) { cctx, ccancel := context.WithCancel(context.Background()) ccancel() - c := newDiagnosticDataCollector(cctx, client, logrus.New(), true, ti) + c := newDiagnosticDataCollector(cctx, client, logger, true, ti, dbBuildInfo) // it should not panic helpers.CollectMetrics(c) } @@ -415,7 +433,10 @@ func TestDisconnectedDiagnosticDataCollector(t *testing.T) { ti := labelsGetterMock{} - c := newDiagnosticDataCollector(ctx, client, logger, true, ti) + dbBuildInfo, err := retrieveMongoDBBuildInfo(ctx, client, logger.WithField("component", "test")) + require.NoError(t, err) + + c := newDiagnosticDataCollector(ctx, client, logger, true, ti, dbBuildInfo) // The last \n at the end of this string is important expected := strings.NewReader(` diff --git a/exporter/encryption_info_test.go b/exporter/encryption_info_test.go index de1b46923..c1565d06f 100644 --- a/exporter/encryption_info_test.go +++ b/exporter/encryption_info_test.go @@ -25,6 +25,7 @@ import ( "github.com/prometheus/client_golang/prometheus/testutil" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/percona/mongodb_exporter/internal/tu" ) @@ -45,7 +46,10 @@ func TestGetEncryptionInfo(t *testing.T) { ti := labelsGetterMock{} - c := newDiagnosticDataCollector(ctx, client, logger, true, ti) + dbBuildInfo, err := retrieveMongoDBBuildInfo(ctx, client, logger.WithField("component", "test")) + require.NoError(t, err) + + c := newDiagnosticDataCollector(ctx, client, logger, true, ti, dbBuildInfo) // The last \n at the end of this string is important expected := strings.NewReader(` @@ -61,6 +65,6 @@ func TestGetEncryptionInfo(t *testing.T) { "mongodb_version_info", } - err := testutil.CollectAndCompare(c, expected, filter...) + err = testutil.CollectAndCompare(c, expected, filter...) assert.NoError(t, err) } diff --git a/exporter/exporter.go b/exporter/exporter.go index 98e62b4db..faa3d2b4c 100644 --- a/exporter/exporter.go +++ b/exporter/exporter.go @@ -28,7 +28,6 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/sirupsen/logrus" - "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "github.com/percona/mongodb_exporter/exporter/dsn_fix" @@ -130,18 +129,6 @@ func (e *Exporter) getTotalCollectionsCount() int { return e.totalCollectionsCount } -type MongoVersion struct { - VersionString string `bson:"version"` - PSMDBVersion string `bson:"psmdbVersion"` - Version []int `bson:"versionArray"` -} - -func getMongoVersion(ctx context.Context, client *mongo.Client) (*MongoVersion, error) { - var ver MongoVersion - err := client.Database("admin").RunCommand(ctx, bson.D{{"buildInfo", 1}}).Decode(&ver) - return &ver, err -} - func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topologyInfo labelsGetter, requestOpts Opts) *prometheus.Registry { registry := prometheus.NewRegistry() @@ -150,9 +137,9 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol e.logger.Errorf("Registry - Cannot get node type : %s", err) } - version, err := getMongoVersion(ctx, client) + dbBuildInfo, err := retrieveMongoDBBuildInfo(ctx, client, e.logger.WithField("component", "buildInfo")) if err != nil { - e.logger.Warnf("Registry - Cannot get MongoDB version: %s", err) + e.logger.Warnf("Registry - Cannot get MongoDB buildInfo: %s", err) } gc := newGeneralCollector(ctx, client, nodeType, e.opts.Logger) @@ -221,7 +208,7 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol if e.opts.EnableDiagnosticData && requestOpts.EnableDiagnosticData { ddc := newDiagnosticDataCollector(ctx, client, e.opts.Logger, - e.opts.CompatibleMode, topologyInfo) + e.opts.CompatibleMode, topologyInfo, dbBuildInfo) registry.MustRegister(ddc) } @@ -252,7 +239,7 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol // replSetGetStatus is not supported through mongos. if e.opts.EnableReplicasetStatus && nodeType != typeMongos && requestOpts.EnableReplicasetStatus { rsgsc := newReplicationSetStatusCollector(ctx, client, e.opts.Logger, - e.opts.CompatibleMode, topologyInfo, version) + e.opts.CompatibleMode, topologyInfo, dbBuildInfo) registry.MustRegister(rsgsc) } diff --git a/exporter/exporter_test.go b/exporter/exporter_test.go index 0b1a6db0d..ccc788aff 100644 --- a/exporter/exporter_test.go +++ b/exporter/exporter_test.go @@ -188,7 +188,7 @@ func TestMongoS(t *testing.T) { e := New(exporterOpts) - version, err := getMongoVersion(ctx, client) + version, err := retrieveMongoDBBuildInfo(ctx, client, exporterOpts.Logger.WithField("component", "test")) require.NoError(t, err) rsgsc := newReplicationSetStatusCollector(ctx, client, e.opts.Logger, e.opts.CompatibleMode, new(labelsGetterMock), version) diff --git a/exporter/replset_status_collector.go b/exporter/replset_status_collector.go index 58bd5f2dd..5266b7688 100644 --- a/exporter/replset_status_collector.go +++ b/exporter/replset_status_collector.go @@ -37,11 +37,11 @@ type replSetGetStatusCollector struct { compatibleMode bool topologyInfo labelsGetter - version *MongoVersion + version buildInfo } // newReplicationSetStatusCollector creates a collector for statistics on replication set. -func newReplicationSetStatusCollector(ctx context.Context, client *mongo.Client, logger *logrus.Logger, compatible bool, topology labelsGetter, version *MongoVersion) *replSetGetStatusCollector { +func newReplicationSetStatusCollector(ctx context.Context, client *mongo.Client, logger *logrus.Logger, compatible bool, topology labelsGetter, version buildInfo) *replSetGetStatusCollector { return &replSetGetStatusCollector{ ctx: ctx, base: newBaseCollector(client, logger.WithFields(logrus.Fields{"collector": "replset_status"})), @@ -89,12 +89,12 @@ func (d *replSetGetStatusCollector) collect(ch chan<- prometheus.Metric) { ch <- metric } - if strings.HasPrefix(d.version.VersionString, "8.") { + if strings.HasPrefix(d.version.Version, "8.") { for _, metric := range makeMetrics("rs", m, d.topologyInfo.baseLabels(), d.compatibleMode) { ch <- metric } if d.compatibleMode { - logger.Infof("collecting compatibility metrics for version %s", d.version.VersionString) + logger.Infof("collecting compatibility metrics for version %s", d.version.Version) metrics := replSetMetrics(m, logger) for _, metric := range metrics { ch <- metric diff --git a/exporter/replset_status_collector_test.go b/exporter/replset_status_collector_test.go index c25da16ca..789252adc 100644 --- a/exporter/replset_status_collector_test.go +++ b/exporter/replset_status_collector_test.go @@ -37,7 +37,7 @@ func TestReplsetStatusCollector(t *testing.T) { ti := labelsGetterMock{} - version, err := getMongoVersion(ctx, client) + version, err := retrieveMongoDBBuildInfo(ctx, client, logrus.New().WithField("component", "test")) require.NoError(t, err) c := newReplicationSetStatusCollector(ctx, client, logrus.New(), false, ti, version) @@ -69,7 +69,7 @@ func TestReplsetStatusCollectorNoSharding(t *testing.T) { ti := labelsGetterMock{} - version, err := getMongoVersion(ctx, client) + version, err := retrieveMongoDBBuildInfo(ctx, client, logrus.New().WithField("component", "test")) require.NoError(t, err) c := newReplicationSetStatusCollector(ctx, client, logrus.New(), false, ti, version) From f33a9cedc6c5c2daaa8cf61162c1ded6ba5806d5 Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Sat, 2 Nov 2024 15:58:10 +0300 Subject: [PATCH 07/15] PMM-13477 Fix tests. --- exporter/diagnostic_data_collector_test.go | 4 ++-- exporter/v1_compatibility.go | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/exporter/diagnostic_data_collector_test.go b/exporter/diagnostic_data_collector_test.go index d72bb882f..cafbc468d 100644 --- a/exporter/diagnostic_data_collector_test.go +++ b/exporter/diagnostic_data_collector_test.go @@ -421,7 +421,7 @@ func cleanTestData(ctx context.Context, client *mongo.Client, count int) error { } func TestDisconnectedDiagnosticDataCollector(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() client := tu.DefaultTestClient(ctx, t) @@ -434,7 +434,7 @@ func TestDisconnectedDiagnosticDataCollector(t *testing.T) { ti := labelsGetterMock{} dbBuildInfo, err := retrieveMongoDBBuildInfo(ctx, client, logger.WithField("component", "test")) - require.NoError(t, err) + require.Error(t, err) c := newDiagnosticDataCollector(ctx, client, logger, true, ti, dbBuildInfo) diff --git a/exporter/v1_compatibility.go b/exporter/v1_compatibility.go index 9dd56c8e3..66334f858 100644 --- a/exporter/v1_compatibility.go +++ b/exporter/v1_compatibility.go @@ -852,6 +852,9 @@ func specialMetrics(ctx context.Context, client *mongo.Client, m bson.M, nodeTyp } func retrieveMongoDBBuildInfo(ctx context.Context, client *mongo.Client, l *logrus.Entry) (buildInfo, error) { + if client == nil { + return buildInfo{}, errors.New("cannot get mongo build info: client is nil") + } buildInfoCmd := bson.D{bson.E{Key: "buildInfo", Value: 1}} res := client.Database("admin").RunCommand(ctx, buildInfoCmd) From 32e6ea4489c02e4555cdfcfc6df79c1140a89def Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Sat, 2 Nov 2024 17:34:40 +0300 Subject: [PATCH 08/15] PMM-13477 Fix fcv tests. --- exporter/feature_compatibility_version_collector_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/exporter/feature_compatibility_version_collector_test.go b/exporter/feature_compatibility_version_collector_test.go index 5196a8888..df2e9e264 100644 --- a/exporter/feature_compatibility_version_collector_test.go +++ b/exporter/feature_compatibility_version_collector_test.go @@ -55,6 +55,14 @@ func TestFCVCollector(t *testing.T) { mversion = "4.4" case mmv == "4.4": mversion = "4.2" + case mmv == "6.0": + mversion = "5.0" + case mmv == "7.0": + mversion = "6.0" + case mmv == "8.0": + mversion = "7.0" + default: + mversion = mmv } // The last \n at the end of this string is important From 7e6c8762f4220024ad97c801cecee1e31695e834 Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Mon, 4 Nov 2024 13:11:14 +0300 Subject: [PATCH 09/15] PMM-13477 see logs on failure. --- .github/workflows/go.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 43ca4b205..9b9d81b4c 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -49,3 +49,17 @@ jobs: sleep 10 make test-race make test-cluster-clean + + - name: Run debug commands on failure + if: ${{ failure() }} + run: | + echo "--- Environment variables ---" + env | sort + echo "--- GO Environment ---" + go env | sort + echo "--- Git status ---" + git status + echo "--- Docker logs ---" + docker compose logs + echo "--- Docker ps ---" + docker compose ps -a From a6a06a1cdc5a525cbd2dfce3fd974d6ae0b498eb Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Mon, 4 Nov 2024 19:35:55 +0300 Subject: [PATCH 10/15] PMM-13477 fix tests. --- exporter/diagnostic_data_collector_test.go | 18 +++++----- exporter/metrics.go | 2 ++ exporter/v1_compatibility.go | 39 +++++++++------------- 3 files changed, 27 insertions(+), 32 deletions(-) diff --git a/exporter/diagnostic_data_collector_test.go b/exporter/diagnostic_data_collector_test.go index cafbc468d..2f6f8a334 100644 --- a/exporter/diagnostic_data_collector_test.go +++ b/exporter/diagnostic_data_collector_test.go @@ -51,24 +51,26 @@ func TestDiagnosticDataCollector(t *testing.T) { c := newDiagnosticDataCollector(ctx, client, logger, false, ti, dbBuildInfo) + prefix := "local.oplog.rs.stats.storageStats.wiredTiger" + if dbBuildInfo.VersionArray[0] < 7 { + prefix = "local.oplog.rs.stats.wiredTiger" + } + // The last \n at the end of this string is important - expected := strings.NewReader(` - # HELP mongodb_oplog_stats_ok local.oplog.rs.stats. - # TYPE mongodb_oplog_stats_ok untyped - mongodb_oplog_stats_ok 1 - # HELP mongodb_oplog_stats_wt_btree_fixed_record_size local.oplog.rs.stats.wiredTiger.btree. + expectedString := fmt.Sprintf(` + # HELP mongodb_oplog_stats_wt_btree_fixed_record_size %s.btree. # TYPE mongodb_oplog_stats_wt_btree_fixed_record_size untyped mongodb_oplog_stats_wt_btree_fixed_record_size 0 - # HELP mongodb_oplog_stats_wt_transaction_update_conflicts local.oplog.rs.stats.wiredTiger.transaction. + # HELP mongodb_oplog_stats_wt_transaction_update_conflicts %s.transaction. # TYPE mongodb_oplog_stats_wt_transaction_update_conflicts untyped - mongodb_oplog_stats_wt_transaction_update_conflicts 0` + "\n") + mongodb_oplog_stats_wt_transaction_update_conflicts 0`, prefix, prefix) + expected := strings.NewReader(expectedString + "\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_oplog_stats_ok", "mongodb_oplog_stats_wt_btree_fixed_record_size", "mongodb_oplog_stats_wt_transaction_update_conflicts", } diff --git a/exporter/metrics.go b/exporter/metrics.go index a6316e461..684509522 100644 --- a/exporter/metrics.go +++ b/exporter/metrics.go @@ -57,7 +57,9 @@ var ( {"replSetGetStatus", "rs"}, {"systemMetrics", "sys"}, {"local.oplog.rs.stats.wiredTiger", "oplog_stats_wt"}, + {"local.oplog.rs.stats.storageStats.wiredTiger", "oplog_stats_wt"}, {"local.oplog.rs.stats", "oplog_stats"}, + {"local.oplog.rs.stats.storageStats", "oplog_stats"}, {"collstats_storage.wiredTiger", "collstats_storage_wt"}, {"collstats_storage.indexDetails", "collstats_storage_idx"}, {"collStats.storageStats", "collstats_storage"}, diff --git a/exporter/v1_compatibility.go b/exporter/v1_compatibility.go index 66334f858..29fc2c7b5 100644 --- a/exporter/v1_compatibility.go +++ b/exporter/v1_compatibility.go @@ -858,38 +858,26 @@ func retrieveMongoDBBuildInfo(ctx context.Context, client *mongo.Client, l *logr buildInfoCmd := bson.D{bson.E{Key: "buildInfo", Value: 1}} res := client.Database("admin").RunCommand(ctx, buildInfoCmd) - var buildInfoDoc bson.M + var buildInfoDoc buildInfo err := res.Decode(&buildInfoDoc) if err != nil { return buildInfo{}, errors.Wrap(err, "Failed to run buildInfo command") } - modules, ok := buildInfoDoc["modules"].(bson.A) - if !ok { - return buildInfo{}, errors.Wrap(err, "Failed to cast module information variable") - } - - var bi buildInfo - if len(modules) > 0 && modules[0].(string) == "enterprise" { - bi.Edition = EnterpriseEdition + if len(buildInfoDoc.Modules) > 0 && buildInfoDoc.Modules[0] == "enterprise" { + buildInfoDoc.Edition = EnterpriseEdition } else { - bi.Edition = CommunityEdition + buildInfoDoc.Edition = CommunityEdition } - l.Debug("MongoDB edition: ", bi.Edition) + l.Debug("MongoDB edition: ", buildInfoDoc.Edition) - _, ok = buildInfoDoc["psmdbVersion"] - if ok { - bi.Vendor = PerconaVendor + if buildInfoDoc.PSMDBVersion != "" { + buildInfoDoc.Vendor = PerconaVendor } else { - bi.Vendor = MongoDBVendor - } - - bi.Version, ok = buildInfoDoc["version"].(string) - if !ok { - return buildInfo{}, errors.Wrap(err, "Failed to cast version information variable") + buildInfoDoc.Vendor = MongoDBVendor } - return bi, nil + return buildInfoDoc, nil } func storageEngine(m bson.M) (prometheus.Metric, error) { //nolint:ireturn @@ -1348,9 +1336,12 @@ type rawStatus struct { } type buildInfo struct { - Version string - Edition string - Vendor string + Version string `bson:"version"` + PSMDBVersion string `bson:"psmdbVersion"` + VersionArray []int `bson:"versionArray"` + Edition string + Vendor string + Modules []string `bson:"modules"` } func getDatabaseStatList(ctx context.Context, client *mongo.Client, l *logrus.Entry) *databaseStatList { From 38fa4f05cc51ae545b82cd6c98a16b790850784a Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Mon, 4 Nov 2024 19:58:50 +0300 Subject: [PATCH 11/15] PMM-13477 fix tests. --- exporter/diagnostic_data_collector.go | 18 +++++++++++++++--- exporter/exporter.go | 2 +- exporter/exporter_test.go | 9 ++------- exporter/replset_status_collector.go | 20 +------------------- exporter/replset_status_collector_test.go | 15 ++++----------- 5 files changed, 23 insertions(+), 41 deletions(-) diff --git a/exporter/diagnostic_data_collector.go b/exporter/diagnostic_data_collector.go index 49d19f197..c8f57dd81 100644 --- a/exporter/diagnostic_data_collector.go +++ b/exporter/diagnostic_data_collector.go @@ -112,13 +112,25 @@ func (d *diagnosticDataCollector) collect(ch chan<- prometheus.Metric) { err = errors.Wrapf(errUnexpectedDataType, "%T for data field", m["data"]) logger.Errorf("cannot decode getDiagnosticData: %s", err) } - if c, ok := m["common"].(bson.M); ok { - m = c - } logger.Debug("getDiagnosticData result") debugResult(logger, m) + // MongoDB 8.0 splits the diagnostic data into multiple blocks, so we need to merge them + if d.buildInfo.VersionArray[0] >= 8 { + b := bson.M{} + for _, mv := range m { + block, ok := mv.(bson.M) + if !ok { + continue + } + for k, v := range block { + b[k] = v + } + } + m = b + } + metrics = makeMetrics("", m, d.topologyInfo.baseLabels(), d.compatibleMode) metrics = append(metrics, locksMetrics(logger, m)...) diff --git a/exporter/exporter.go b/exporter/exporter.go index faa3d2b4c..23e939923 100644 --- a/exporter/exporter.go +++ b/exporter/exporter.go @@ -239,7 +239,7 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol // replSetGetStatus is not supported through mongos. if e.opts.EnableReplicasetStatus && nodeType != typeMongos && requestOpts.EnableReplicasetStatus { rsgsc := newReplicationSetStatusCollector(ctx, client, e.opts.Logger, - e.opts.CompatibleMode, topologyInfo, dbBuildInfo) + e.opts.CompatibleMode, topologyInfo) registry.MustRegister(rsgsc) } diff --git a/exporter/exporter_test.go b/exporter/exporter_test.go index ccc788aff..4260567c0 100644 --- a/exporter/exporter_test.go +++ b/exporter/exporter_test.go @@ -27,12 +27,10 @@ import ( "sync" "testing" + "github.com/percona/mongodb_exporter/internal/tu" "github.com/prometheus/client_golang/prometheus/testutil" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/percona/mongodb_exporter/internal/tu" ) // Use this for testing because labels like cluster ID are not constant in docker containers @@ -187,10 +185,7 @@ func TestMongoS(t *testing.T) { assert.NoError(t, err) e := New(exporterOpts) - - version, err := retrieveMongoDBBuildInfo(ctx, client, exporterOpts.Logger.WithField("component", "test")) - require.NoError(t, err) - rsgsc := newReplicationSetStatusCollector(ctx, client, e.opts.Logger, e.opts.CompatibleMode, new(labelsGetterMock), version) + rsgsc := newReplicationSetStatusCollector(ctx, client, e.opts.Logger, e.opts.CompatibleMode, new(labelsGetterMock)) r := e.makeRegistry(ctx, client, new(labelsGetterMock), *e.opts) diff --git a/exporter/replset_status_collector.go b/exporter/replset_status_collector.go index 5266b7688..9e4d75e15 100644 --- a/exporter/replset_status_collector.go +++ b/exporter/replset_status_collector.go @@ -17,8 +17,6 @@ package exporter import ( "context" - "strings" - "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" "go.mongodb.org/mongo-driver/bson" @@ -36,19 +34,16 @@ type replSetGetStatusCollector struct { compatibleMode bool topologyInfo labelsGetter - - version buildInfo } // newReplicationSetStatusCollector creates a collector for statistics on replication set. -func newReplicationSetStatusCollector(ctx context.Context, client *mongo.Client, logger *logrus.Logger, compatible bool, topology labelsGetter, version buildInfo) *replSetGetStatusCollector { +func newReplicationSetStatusCollector(ctx context.Context, client *mongo.Client, logger *logrus.Logger, compatible bool, topology labelsGetter) *replSetGetStatusCollector { return &replSetGetStatusCollector{ ctx: ctx, base: newBaseCollector(client, logger.WithFields(logrus.Fields{"collector": "replset_status"})), compatibleMode: compatible, topologyInfo: topology, - version: version, } } @@ -88,19 +83,6 @@ func (d *replSetGetStatusCollector) collect(ch chan<- prometheus.Metric) { for _, metric := range makeMetrics("", m, d.topologyInfo.baseLabels(), d.compatibleMode) { ch <- metric } - - if strings.HasPrefix(d.version.Version, "8.") { - for _, metric := range makeMetrics("rs", m, d.topologyInfo.baseLabels(), d.compatibleMode) { - ch <- metric - } - if d.compatibleMode { - logger.Infof("collecting compatibility metrics for version %s", d.version.Version) - metrics := replSetMetrics(m, logger) - for _, metric := range metrics { - ch <- metric - } - } - } } var _ prometheus.Collector = (*replSetGetStatusCollector)(nil) diff --git a/exporter/replset_status_collector_test.go b/exporter/replset_status_collector_test.go index 789252adc..ce7682f80 100644 --- a/exporter/replset_status_collector_test.go +++ b/exporter/replset_status_collector_test.go @@ -21,12 +21,10 @@ import ( "testing" "time" + "github.com/percona/mongodb_exporter/internal/tu" "github.com/prometheus/client_golang/prometheus/testutil" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/percona/mongodb_exporter/internal/tu" ) func TestReplsetStatusCollector(t *testing.T) { @@ -37,9 +35,7 @@ func TestReplsetStatusCollector(t *testing.T) { ti := labelsGetterMock{} - version, err := retrieveMongoDBBuildInfo(ctx, client, logrus.New().WithField("component", "test")) - require.NoError(t, err) - c := newReplicationSetStatusCollector(ctx, client, logrus.New(), false, ti, version) + c := newReplicationSetStatusCollector(ctx, client, logrus.New(), false, ti) // The last \n at the end of this string is important expected := strings.NewReader(` @@ -57,7 +53,7 @@ func TestReplsetStatusCollector(t *testing.T) { "mongodb_myState", "mongodb_ok", } - err = testutil.CollectAndCompare(c, expected, filter...) + err := testutil.CollectAndCompare(c, expected, filter...) assert.NoError(t, err) } @@ -69,10 +65,7 @@ func TestReplsetStatusCollectorNoSharding(t *testing.T) { ti := labelsGetterMock{} - version, err := retrieveMongoDBBuildInfo(ctx, client, logrus.New().WithField("component", "test")) - require.NoError(t, err) - - c := newReplicationSetStatusCollector(ctx, client, logrus.New(), false, ti, version) + c := newReplicationSetStatusCollector(ctx, client, logrus.New(), false, ti) // Replication set metrics should not be generated for unsharded server count := testutil.CollectAndCount(c) From 0ff564eaf79a5e0d90f7040300bc7f14283c75bd Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Mon, 4 Nov 2024 20:03:43 +0300 Subject: [PATCH 12/15] PMM-13477 fix imports. --- exporter/exporter_test.go | 3 ++- exporter/replset_status_collector.go | 1 + exporter/replset_status_collector_test.go | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/exporter/exporter_test.go b/exporter/exporter_test.go index 4260567c0..a00ca19d8 100644 --- a/exporter/exporter_test.go +++ b/exporter/exporter_test.go @@ -27,10 +27,11 @@ import ( "sync" "testing" - "github.com/percona/mongodb_exporter/internal/tu" "github.com/prometheus/client_golang/prometheus/testutil" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" + + "github.com/percona/mongodb_exporter/internal/tu" ) // Use this for testing because labels like cluster ID are not constant in docker containers diff --git a/exporter/replset_status_collector.go b/exporter/replset_status_collector.go index 9e4d75e15..afbbd6d39 100644 --- a/exporter/replset_status_collector.go +++ b/exporter/replset_status_collector.go @@ -17,6 +17,7 @@ package exporter import ( "context" + "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" "go.mongodb.org/mongo-driver/bson" diff --git a/exporter/replset_status_collector_test.go b/exporter/replset_status_collector_test.go index ce7682f80..12a5cbcb0 100644 --- a/exporter/replset_status_collector_test.go +++ b/exporter/replset_status_collector_test.go @@ -21,10 +21,11 @@ import ( "testing" "time" - "github.com/percona/mongodb_exporter/internal/tu" "github.com/prometheus/client_golang/prometheus/testutil" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" + + "github.com/percona/mongodb_exporter/internal/tu" ) func TestReplsetStatusCollector(t *testing.T) { From 60ac3f3db5e9fe36979259e22f7a2ca5cdac8d99 Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Mon, 4 Nov 2024 20:29:03 +0300 Subject: [PATCH 13/15] PMM-13477 a bit of refactoring. --- exporter/exporter.go | 11 ++++++----- exporter/general_collector.go | 17 ++++++++--------- exporter/server.go | 10 ++++++++-- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/exporter/exporter.go b/exporter/exporter.go index 23e939923..52af296a9 100644 --- a/exporter/exporter.go +++ b/exporter/exporter.go @@ -145,10 +145,6 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol gc := newGeneralCollector(ctx, client, nodeType, e.opts.Logger) registry.MustRegister(gc) - if client == nil { - return registry - } - // Enable collectors like collstats and indexstats depending on the number of collections // present in the database. limitsOk := false @@ -340,13 +336,18 @@ func (e *Exporter) Handler() http.Handler { gatherers = append(gatherers, prometheus.DefaultGatherer) } + var registry *prometheus.Registry var ti *topologyInfo if client != nil { // Topology can change between requests, so we need to get it every time. ti = newTopologyInfo(ctx, client, e.logger) + registry = e.makeRegistry(ctx, client, ti, requestOpts) + } else { + registry = prometheus.NewRegistry() + gc := newGeneralCollector(ctx, client, "", e.opts.Logger) + registry.MustRegister(gc) } - registry := e.makeRegistry(ctx, client, ti, requestOpts) gatherers = append(gatherers, registry) // Delegate http serving to Prometheus client library, which will call collector.Collect. diff --git a/exporter/general_collector.go b/exporter/general_collector.go index f7218f6e7..3df63927a 100644 --- a/exporter/general_collector.go +++ b/exporter/general_collector.go @@ -60,18 +60,17 @@ func mongodbUpMetric(ctx context.Context, client *mongo.Client, nodeType mongoDB if client != nil { if err := client.Ping(ctx, readpref.PrimaryPreferred()); err == nil { value = 1 + switch nodeType { //nolint:exhaustive + case typeMongos: + clusterRole = typeMongos + case typeArbiter: + clusterRole = typeArbiter + default: + clusterRole = typeMongod + } } else { log.Errorf("error while checking mongodb connection: %s. mongo_up is set to 0", err.Error()) } - - switch nodeType { //nolint:exhaustive - case typeMongos: - clusterRole = typeMongos - case typeArbiter: - clusterRole = typeArbiter - default: - clusterRole = typeMongod - } } labels := map[string]string{"cluster_role": string(clusterRole)} diff --git a/exporter/server.go b/exporter/server.go index cb19b07eb..5c535c505 100644 --- a/exporter/server.go +++ b/exporter/server.go @@ -147,18 +147,24 @@ func OverallTargetsHandler(exporters []*Exporter, logger *logrus.Logger) http.Ha }() } + var registry *prometheus.Registry var ti *topologyInfo if client != nil { // Topology can change between requests, so we need to get it every time. ti = newTopologyInfo(ctx, client, e.logger) + registry = e.makeRegistry(ctx, client, ti, requestOpts) + } else { + registry = prometheus.NewRegistry() + gc := newGeneralCollector(ctx, client, "", e.opts.Logger) + registry.MustRegister(gc) } hostlabels := prometheus.Labels{ "instance": e.opts.NodeName, } - registry := NewGathererWrapper(e.makeRegistry(ctx, client, ti, requestOpts), hostlabels) - gatherers = append(gatherers, registry) + gw := NewGathererWrapper(registry, hostlabels) + gatherers = append(gatherers, gw) } // Delegate http serving to Prometheus client library, which will call collector.Collect. From 17cec7cc1ce02f2b50e9af292e7566d8c184d94c Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Mon, 4 Nov 2024 20:36:21 +0300 Subject: [PATCH 14/15] PMM-13477 fix test. --- exporter/general_collector.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/exporter/general_collector.go b/exporter/general_collector.go index 3df63927a..ebe9f3f4f 100644 --- a/exporter/general_collector.go +++ b/exporter/general_collector.go @@ -60,17 +60,15 @@ func mongodbUpMetric(ctx context.Context, client *mongo.Client, nodeType mongoDB if client != nil { if err := client.Ping(ctx, readpref.PrimaryPreferred()); err == nil { value = 1 - switch nodeType { //nolint:exhaustive - case typeMongos: - clusterRole = typeMongos - case typeArbiter: - clusterRole = typeArbiter - default: - clusterRole = typeMongod - } } else { log.Errorf("error while checking mongodb connection: %s. mongo_up is set to 0", err.Error()) } + switch nodeType { //nolint:exhaustive + case typeShardServer: + clusterRole = typeMongod + default: + clusterRole = nodeType + } } labels := map[string]string{"cluster_role": string(clusterRole)} From 95b0bd285604da689bf51d12390131a29b77ac24 Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Tue, 5 Nov 2024 12:34:28 +0300 Subject: [PATCH 15/15] Update exporter/diagnostic_data_collector.go Co-authored-by: Alex Demidoff --- exporter/diagnostic_data_collector.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exporter/diagnostic_data_collector.go b/exporter/diagnostic_data_collector.go index c8f57dd81..57b4b7e96 100644 --- a/exporter/diagnostic_data_collector.go +++ b/exporter/diagnostic_data_collector.go @@ -117,7 +117,7 @@ func (d *diagnosticDataCollector) collect(ch chan<- prometheus.Metric) { debugResult(logger, m) // MongoDB 8.0 splits the diagnostic data into multiple blocks, so we need to merge them - if d.buildInfo.VersionArray[0] >= 8 { + if d.buildInfo.VersionArray[0] >= 8 { //nolint:gomnd b := bson.M{} for _, mv := range m { block, ok := mv.(bson.M)