Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PMM-13477 Support MongoDB 8.0 #943

Merged
merged 18 commits into from
Nov 5, 2024
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
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 @@
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 @@
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 @@
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

Check failure on line 120 in exporter/diagnostic_data_collector.go

View workflow job for this annotation

GitHub Actions / Lint Check

Magic number: 8, in <condition> detected (mnd)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [golangci-lint] reported by reviewdog 🐶
Magic number: 8, in detected (mnd)

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

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
Loading