Skip to content

Commit

Permalink
prometheus: improve flatten key performance.
Browse files Browse the repository at this point in the history
The flattenKey function is called whenever a gauge, counter, or
sample is set when using the Prometheus sink. The change most
notably uses a replacer rather than a regex. The hash formatting
also now uses + for concatenation rather than fmt.Sprintf for an
addition perf improvement.
  • Loading branch information
jrasell committed Sep 8, 2022
1 parent 129ee86 commit 2a21e9d
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 4 deletions.
7 changes: 3 additions & 4 deletions prometheus/prometheus.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package prometheus
import (
"fmt"
"log"
"regexp"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -248,15 +247,15 @@ func initCounters(m *sync.Map, counters []CounterDefinition, help map[string]str
return
}

var forbiddenChars = regexp.MustCompile("[ .=\\-/]")
var forbiddenCharsReplacer = strings.NewReplacer(" ", "_", ".", "_", "=", "_", "-", "_", "/", "_")

func flattenKey(parts []string, labels []metrics.Label) (string, string) {
key := strings.Join(parts, "_")
key = forbiddenChars.ReplaceAllString(key, "_")
key = forbiddenCharsReplacer.Replace(key)

hash := key
for _, label := range labels {
hash += fmt.Sprintf(";%s=%s", label.Name, label.Value)
hash += ";" + label.Name + "=" + label.Value
}

return key, hash
Expand Down
83 changes: 83 additions & 0 deletions prometheus/prometheus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,86 @@ func TestMetricSinkInterface(t *testing.T) {
var pps *PrometheusPushSink
_ = metrics.MetricSink(pps)
}

func Test_flattenKey(t *testing.T) {
testCases := []struct {
name string
inputParts []string
inputLabels []metrics.Label
expectedOutputKey string
expectedOutputHash string
}{
{
name: "no replacement needed",
inputParts: []string{"my", "example", "metric"},
inputLabels: []metrics.Label{
{Name: "foo", Value: "bar"},
{Name: "baz", Value: "buz"},
},
expectedOutputKey: "my_example_metric",
expectedOutputHash: "my_example_metric;foo=bar;baz=buz",
},
{
name: "key with whitespace",
inputParts: []string{" my ", " example ", " metric "},
inputLabels: []metrics.Label{
{Name: "foo", Value: "bar"},
{Name: "baz", Value: "buz"},
},
expectedOutputKey: "_my___example___metric_",
expectedOutputHash: "_my___example___metric_;foo=bar;baz=buz",
},
{
name: "key with dot",
inputParts: []string{".my.", ".example.", ".metric."},
inputLabels: []metrics.Label{
{Name: "foo", Value: "bar"},
{Name: "baz", Value: "buz"},
},
expectedOutputKey: "_my___example___metric_",
expectedOutputHash: "_my___example___metric_;foo=bar;baz=buz",
},
{
name: "key with dash",
inputParts: []string{"-my-", "-example-", "-metric-"},
inputLabels: []metrics.Label{
{Name: "foo", Value: "bar"},
{Name: "baz", Value: "buz"},
},
expectedOutputKey: "_my___example___metric_",
expectedOutputHash: "_my___example___metric_;foo=bar;baz=buz",
},
{
name: "key with forward slash",
inputParts: []string{"/my/", "/example/", "/metric/"},
inputLabels: []metrics.Label{
{Name: "foo", Value: "bar"},
{Name: "baz", Value: "buz"},
},
expectedOutputKey: "_my___example___metric_",
expectedOutputHash: "_my___example___metric_;foo=bar;baz=buz",
},
{
name: "key with all restricted",
inputParts: []string{"/my-", ".example ", "metric"},
inputLabels: []metrics.Label{
{Name: "foo", Value: "bar"},
{Name: "baz", Value: "buz"},
},
expectedOutputKey: "_my___example__metric",
expectedOutputHash: "_my___example__metric;foo=bar;baz=buz",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(b *testing.T) {
actualKey, actualHash := flattenKey(tc.inputParts, tc.inputLabels)
if actualKey != tc.expectedOutputKey {
t.Fatalf("expected key %s, got %s", tc.expectedOutputKey, actualKey)
}
if actualHash != tc.expectedOutputHash {
t.Fatalf("expected hash %s, got %s", tc.expectedOutputHash, actualHash)
}
})
}
}

0 comments on commit 2a21e9d

Please sign in to comment.