Repository: prometheus/statsd_exporter
Pull request #286 (2020-01-11 12:05:09)

Use correct name when multiple names match same FSM match
Fixes https://github.com/prometheus/statsd_exporter/issues/273

Make shallow copy of FSM result so that we do not overwrite name.

Before shallow copy:

```
$ go test -v -run='^(TestMultipleMatches)$' ./pkg/mapper/
=== RUN   TestMultipleMatches
--- FAIL: TestMultipleMatches (0.00s)
    mapper_test.go:936: 1:1 Expected name aa_bb_dd_total, got aa_bb_cc_total
FAIL
FAIL	github.com/prometheus/statsd_exporter/pkg/mapper	0.260s
FAIL
```

After shallow copy:
```
$ go test -v -run='^(TestMultipleMatches)$' ./pkg/mapper/
=== RUN   TestMultipleMatches
--- PASS: TestMultipleMatches (0.00s)
PASS
ok  	github.com/prometheus/statsd_exporter/pkg/mapper	(cached)
```


Patch:
diff --git a/pkg/mapper/mapper.go b/pkg/mapper/mapper.go
index 808f87d..6496310 100644
--- a/pkg/mapper/mapper.go
+++ b/pkg/mapper/mapper.go
@@ -245,7 +245,8 @@ func (m *MetricMapper) GetMapping(statsdMetric string, statsdMetricType MetricTy
 	if m.doFSM {
 		finalState, captures := m.FSM.GetMapping(statsdMetric, string(statsdMetricType))
 		if finalState != nil && finalState.Result != nil {
-			result := finalState.Result.(*MetricMapping)
+			v := finalState.Result.(*MetricMapping)
+			result := copyMetricMapping(v)
 			result.Name = result.nameFormatter.Format(captures)
 
 			labels := prometheus.Labels{}
@@ -299,3 +300,11 @@ func (m *MetricMapper) GetMapping(statsdMetric string, statsdMetricType MetricTy
 	m.cache.AddMiss(statsdMetric, statsdMetricType)
 	return nil, nil, false
 }
+
+// make a shallow copy so that we do not overwrite name
+// as multiple names can be matched by same mapping
+func copyMetricMapping(in *MetricMapping) *MetricMapping {
+	var out MetricMapping
+	out = *in
+	return &out
+}


Test patch:
diff --git a/pkg/mapper/mapper_test.go b/pkg/mapper/mapper_test.go
index 93649c6..3eb0d1c 100644
--- a/pkg/mapper/mapper_test.go
+++ b/pkg/mapper/mapper_test.go
@@ -887,3 +887,56 @@ mappings:
 		}
 	}
 }
+
+// Test for https://github.com/prometheus/statsd_exporter/issues/273
+// Corrupt cache for multiple names matching in fsm
+func TestMultipleMatches(t *testing.T) {
+	config := `---
+mappings:
+- match: aa.bb.*.*
+  name: "aa_bb_${1}_total"
+  labels:
+  app: "$2"
+`
+	mapper := MetricMapper{}
+	err := mapper.InitFromYAMLString(config, 0)
+	if err != nil {
+		t.Fatalf("config load error: %s ", err)
+	}
+
+	names := map[string]string{
+		"aa.bb.aa.myapp": "aa_bb_aa_total",
+		"aa.bb.bb.myapp": "aa_bb_bb_total",
+		"aa.bb.cc.myapp": "aa_bb_cc_total",
+		"aa.bb.dd.myapp": "aa_bb_dd_total",
+	}
+
+	scenarios := []struct {
+		cacheSize int
+	}{
+		{
+			cacheSize: 0,
+		},
+		{
+			cacheSize: len(names),
+		},
+	}
+
+	for i, scenario := range scenarios {
+		mapper.InitCache(scenario.cacheSize)
+
+		// run multiple times to ensure cache works as expected
+		for j := 0; j < 10; j++ {
+			for name, expected := range names {
+				m, _, ok := mapper.GetMapping(name, MetricTypeCounter)
+				if !ok {
+					t.Fatalf("%d:%d Did not find match for %s", i, j, name)
+				}
+				if m.Name != expected {
+					t.Fatalf("%d:%d Expected name %s, got %s", i, j, expected, m.Name)
+				}
+			}
+		}
+	}
+
+}