Skip to content

Commit

Permalink
PMM-13477 Support MongoDB 8.0 (#943)
Browse files Browse the repository at this point in the history
* PMM-13477 Support MongoDB 8.0

* PMM-13477 Support MongoDB 8.0

* PMM-13477 Support MongoDB 8.0

* PMM-13477 Fix tests.

* PMM-13477 Fix imports and test matrix.

* PMM-13477 A little bit of refactoring.

* PMM-13477 Fix tests.

* PMM-13477 Fix fcv tests.

* PMM-13477 see logs on failure.

* PMM-13477 fix tests.

* PMM-13477 fix tests.

* PMM-13477 fix imports.

* PMM-13477 a bit of refactoring.

* PMM-13477 fix test.

* Update exporter/diagnostic_data_collector.go

Co-authored-by: Alex Demidoff <[email protected]>

---------

Co-authored-by: Alex Demidoff <[email protected]>
  • Loading branch information
BupycHuk and ademidoff authored Nov 5, 2024
1 parent 913bd48 commit 1e917bf
Show file tree
Hide file tree
Showing 14 changed files with 184 additions and 84 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -42,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
28 changes: 21 additions & 7 deletions exporter/diagnostic_data_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand All @@ -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,
}
Expand Down Expand Up @@ -112,6 +116,21 @@ func (d *diagnosticDataCollector) collect(ch chan<- prometheus.Metric) {
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 { //nolint:gomnd
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)...)

Expand All @@ -132,12 +151,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 {
Expand Down
63 changes: 43 additions & 20 deletions exporter/diagnostic_data_collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,31 +46,36 @@ 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)

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",
}

err := testutil.CollectAndCompare(c, expected, filter...)
err = testutil.CollectAndCompare(c, expected, filter...)
assert.NoError(t, err)
}

Expand Down Expand Up @@ -188,7 +193,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)
Expand All @@ -202,12 +210,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)

c := newDiagnosticDataCollector(ctx, client, logrus.New(), 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)
err = reg.Register(c)
require.NoError(t, err)
metrics := helpers.CollectMetrics(c)
actualMetrics := helpers.ReadMetrics(metrics)
Expand Down Expand Up @@ -281,7 +293,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)
Expand Down Expand Up @@ -318,19 +334,23 @@ 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

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)
}
Expand Down Expand Up @@ -403,7 +423,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)
Expand All @@ -415,7 +435,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.Error(t, err)

c := newDiagnosticDataCollector(ctx, client, logger, true, ti, dbBuildInfo)

// The last \n at the end of this string is important
expected := strings.NewReader(`
Expand Down
8 changes: 6 additions & 2 deletions exporter/encryption_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -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(`
Expand All @@ -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)
}
20 changes: 13 additions & 7 deletions exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,16 +134,17 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol

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)
}

dbBuildInfo, err := retrieveMongoDBBuildInfo(ctx, client, e.logger.WithField("component", "buildInfo"))
if err != nil {
e.logger.Warnf("Registry - Cannot get MongoDB buildInfo: %s", err)
}

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
Expand Down Expand Up @@ -203,7 +204,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)
}

Expand Down Expand Up @@ -335,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.
Expand Down
4 changes: 1 addition & 3 deletions exporter/exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,7 @@ func TestMongoS(t *testing.T) {
assert.NoError(t, err)

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))

r := e.makeRegistry(ctx, client, new(labelsGetterMock), *e.opts)

Expand Down
8 changes: 8 additions & 0 deletions exporter/feature_compatibility_version_collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 3 additions & 6 deletions exporter/general_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,11 @@ func mongodbUpMetric(ctx context.Context, client *mongo.Client, nodeType mongoDB
} 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:
case typeShardServer:
clusterRole = typeMongod
default:
clusterRole = nodeType
}
}

Expand Down
4 changes: 4 additions & 0 deletions exporter/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,14 @@ 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"},
{"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"},
Expand Down Expand Up @@ -105,6 +108,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",
}
Expand Down
3 changes: 2 additions & 1 deletion exporter/secondary_lag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"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"
Expand Down Expand Up @@ -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"`) {
Expand Down
Loading

0 comments on commit 1e917bf

Please sign in to comment.