Skip to content

Commit b374de6

Browse files
aravandidenisokShashank Sinha
authored
[feature] Add a Descending index metric name override flag (#435)
Fields with descending order in a MongoDB index have '-1' in index name. '-' is not a valid character in data model for Prometheus. Hence it will be replaced with 'DESC' to remove ambiguity between indexes with use of new flag in exporter. Co-authored-by: Denys Kondratenko <[email protected]> Co-authored-by: Shashank Sinha <[email protected]>
1 parent 572870c commit b374de6

File tree

5 files changed

+74
-9
lines changed

5 files changed

+74
-9
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ the current version.
4242
|\-\-no-collector.replicasetstatus|Disable collecting metrics from replSetGetStatus||
4343
|\-\-collector.dbstats|Enable collecting metrics from dbStats||
4444
|\-\-enable.top|Enable collecting metrics from top admin command||
45+
|\-\-metrics.overridedescendingindex| Enable descending index name override to replace -1 with _DESC ||
4546
|--version|Show version and exit|
4647

4748
### Build the exporter

exporter/exporter.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ type Opts struct {
6565
EnableIndexStats bool
6666
EnableCollStats bool
6767

68+
EnableOverrideDescendingIndex bool
69+
6870
IndexStatsCollections []string
6971
Logger *logrus.Logger
7072
Path string
@@ -165,7 +167,8 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol
165167
// If we manually set the collection names we want or auto discovery is set.
166168
if (len(e.opts.IndexStatsCollections) > 0 || e.opts.DiscoveringMode) && e.opts.EnableIndexStats && limitsOk && requestOpts.EnableIndexStats {
167169
ic := newIndexStatsCollector(ctx, client, e.opts.Logger,
168-
e.opts.DiscoveringMode, topologyInfo, e.opts.IndexStatsCollections)
170+
e.opts.DiscoveringMode, e.opts.EnableOverrideDescendingIndex,
171+
topologyInfo, e.opts.IndexStatsCollections)
169172
registry.MustRegister(ic)
170173
}
171174

exporter/indexstats_collector.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,24 @@ type indexstatsCollector struct {
3131
ctx context.Context
3232
base *baseCollector
3333

34-
discoveringMode bool
35-
topologyInfo labelsGetter
34+
discoveringMode bool
35+
overrideDescendingIndex bool
36+
topologyInfo labelsGetter
3637

3738
collections []string
3839
}
3940

4041
// newIndexStatsCollector creates a collector for statistics on index usage.
41-
func newIndexStatsCollector(ctx context.Context, client *mongo.Client, logger *logrus.Logger, discovery bool, topology labelsGetter, collections []string) *indexstatsCollector {
42+
func newIndexStatsCollector(ctx context.Context, client *mongo.Client, logger *logrus.Logger, discovery, overrideDescendingIndex bool, topology labelsGetter, collections []string) *indexstatsCollector {
4243
return &indexstatsCollector{
4344
ctx: ctx,
4445
base: newBaseCollector(client, logger),
4546

46-
discoveringMode: discovery,
47-
topologyInfo: topology,
48-
collections: collections,
47+
discoveringMode: discovery,
48+
topologyInfo: topology,
49+
overrideDescendingIndex: overrideDescendingIndex,
50+
51+
collections: collections,
4952
}
5053
}
5154

@@ -106,13 +109,19 @@ func (d *indexstatsCollector) collect(ch chan<- prometheus.Metric) {
106109
debugResult(d.base.logger, stats)
107110

108111
for _, metric := range stats {
112+
indexName := fmt.Sprintf("%s", metric["name"])
113+
// Override the label name
114+
if d.overrideDescendingIndex {
115+
indexName = strings.ReplaceAll(fmt.Sprintf("%s", metric["name"]), "-1", "DESC")
116+
}
117+
109118
// prefix and labels are needed to avoid duplicated metric names since the metrics are the
110119
// same, for different collections.
111120
prefix := "indexstats"
112121
labels := d.topologyInfo.baseLabels()
113122
labels["database"] = database
114123
labels["collection"] = collection
115-
labels["key_name"] = fmt.Sprintf("%s", metric["name"])
124+
labels["key_name"] = indexName
116125

117126
metrics := sanitizeMetrics(metric)
118127
for _, metric := range makeMetrics(prefix, metrics, labels, false) {

exporter/indexstats_collector_test.go

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func TestIndexStatsCollector(t *testing.T) {
6565
}
6666

6767
collection := []string{"testdb.testcol_00", "testdb.testcol_01", "testdb.testcol_02"}
68-
c := newIndexStatsCollector(ctx, client, logrus.New(), false, ti, collection)
68+
c := newIndexStatsCollector(ctx, client, logrus.New(), false, true, ti, collection)
6969

7070
// The last \n at the end of this string is important
7171
expected := strings.NewReader(`
@@ -83,6 +83,54 @@ mongodb_indexstats_accesses_ops{collection="testcol_02",database="testdb",key_na
8383
assert.NoError(t, err)
8484
}
8585

86+
func TestDescendingIndexOverride(t *testing.T) {
87+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
88+
defer cancel()
89+
90+
client := tu.DefaultTestClient(ctx, t)
91+
92+
ti := labelsGetterMock{}
93+
94+
database := client.Database("testdb")
95+
database.Drop(ctx) //nolint:errcheck
96+
defer database.Drop(ctx) //nolint:errcheck
97+
98+
for i := 0; i < 3; i++ {
99+
collection := fmt.Sprintf("testcol_%02d", i)
100+
for j := 0; j < 10; j++ {
101+
_, err := database.Collection(collection).InsertOne(ctx, bson.M{"f1": j, "f2": "2"})
102+
assert.NoError(t, err)
103+
}
104+
105+
descendingMod := mongo.IndexModel{Keys: bson.M{"f1": -1}}
106+
_, err := database.Collection(collection).Indexes().CreateOne(ctx, descendingMod)
107+
assert.NoError(t, err)
108+
109+
ascendingMod := mongo.IndexModel{Keys: bson.M{"f1": 1}}
110+
_, err = database.Collection(collection).Indexes().CreateOne(ctx, ascendingMod)
111+
assert.NoError(t, err)
112+
}
113+
114+
collection := []string{"testdb.testcol_00", "testdb.testcol_01", "testdb.testcol_02"}
115+
c := newIndexStatsCollector(ctx, client, logrus.New(), false, true, ti, collection)
116+
117+
// The last \n at the end of this string is important
118+
expected := strings.NewReader(`
119+
# HELP mongodb_indexstats_accesses_ops indexstats.accesses.
120+
# TYPE mongodb_indexstats_accesses_ops untyped
121+
mongodb_indexstats_accesses_ops{collection="testcol_00",database="testdb",key_name="_id_"} 0
122+
mongodb_indexstats_accesses_ops{collection="testcol_00",database="testdb",key_name="f1_1"} 0
123+
mongodb_indexstats_accesses_ops{collection="testcol_00",database="testdb",key_name="f1_DESC"} 0
124+
mongodb_indexstats_accesses_ops{collection="testcol_01",database="testdb",key_name="_id_"} 0
125+
mongodb_indexstats_accesses_ops{collection="testcol_01",database="testdb",key_name="f1_1"} 0
126+
mongodb_indexstats_accesses_ops{collection="testcol_01",database="testdb",key_name="f1_DESC"} 0
127+
mongodb_indexstats_accesses_ops{collection="testcol_02",database="testdb",key_name="_id_"} 0
128+
mongodb_indexstats_accesses_ops{collection="testcol_02",database="testdb",key_name="f1_1"} 0
129+
mongodb_indexstats_accesses_ops{collection="testcol_02",database="testdb",key_name="f1_DESC"} 0` + "\n")
130+
err := testutil.CollectAndCompare(c, expected)
131+
assert.NoError(t, err)
132+
}
133+
86134
func TestSanitize(t *testing.T) {
87135
t.Run("With building", func(t *testing.T) {
88136
in := bson.M{

main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ type GlobalFlags struct {
5151
EnableIndexStats bool `name:"collector.indexstats" help:"Enable collecting metrics from $indexStats"`
5252
EnableCollStats bool `name:"collector.collstats" help:"Enable collecting metrics from $collStats"`
5353

54+
EnableOverrideDescendingIndex bool `name:"metrics.overridedescendingindex" help:"Enable descending index name override to replace -1 with _DESC"`
55+
5456
CollectAll bool `name:"collect-all" help:"Enable all collectors. Same as specifying all --collector.<name>"`
5557

5658
CollStatsLimit int `name:"collector.collstats-limit" help:"Disable collstats, dbstats, topmetrics and indexstats collector if there are more than <n> collections. 0=No limit" default:"0"`
@@ -126,6 +128,8 @@ func buildExporter(opts GlobalFlags) *exporter.Exporter {
126128
EnableIndexStats: opts.EnableIndexStats,
127129
EnableCollStats: opts.EnableCollStats,
128130

131+
EnableOverrideDescendingIndex: opts.EnableOverrideDescendingIndex,
132+
129133
CollStatsLimit: opts.CollStatsLimit,
130134
CollectAll: opts.CollectAll,
131135
}

0 commit comments

Comments
 (0)