From df784917e407951706b3e734a6f5462ff90d9c09 Mon Sep 17 00:00:00 2001 From: Dave Henderson Date: Wed, 5 Jun 2024 09:19:09 -0400 Subject: [PATCH] Alerting: Improve performance of tupleLablesToLabels function (#88736) * Alerting: Improve performance of tupleLablesToLabels function Signed-off-by: Dave Henderson * use %s for string rather than %v Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com> --------- Signed-off-by: Dave Henderson Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com> --- .../ngalert/models/instance_labels.go | 12 +- .../ngalert/models/instance_labels_test.go | 114 ++++++++++++++++++ 2 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 pkg/services/ngalert/models/instance_labels_test.go diff --git a/pkg/services/ngalert/models/instance_labels.go b/pkg/services/ngalert/models/instance_labels.go index a19d8f736f9..468ea3da032 100644 --- a/pkg/services/ngalert/models/instance_labels.go +++ b/pkg/services/ngalert/models/instance_labels.go @@ -100,12 +100,16 @@ func tupleLablesToLabels(tuples tupleLabels) (InstanceLabels, error) { if tuples == nil { return InstanceLabels{}, nil } - labels := make(map[string]string) + + labels := make(map[string]string, len(tuples)) for _, tuple := range tuples { - if key, ok := labels[tuple[0]]; ok { - return nil, fmt.Errorf("duplicate key '%v' in lables: %v", key, tuples) + key, value := tuple[0], tuple[1] + if _, ok := labels[key]; ok { + return nil, fmt.Errorf("duplicate key '%s' in labels: %v", key, tuples) } - labels[tuple[0]] = tuple[1] + + labels[key] = value } + return labels, nil } diff --git a/pkg/services/ngalert/models/instance_labels_test.go b/pkg/services/ngalert/models/instance_labels_test.go new file mode 100644 index 00000000000..513841798de --- /dev/null +++ b/pkg/services/ngalert/models/instance_labels_test.go @@ -0,0 +1,114 @@ +package models + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestTupleLabelsToLabels(t *testing.T) { + t.Run("converts tupleLabels to InstanceLabels", func(t *testing.T) { + in := tupleLabels{ + {"foo", "bar"}, + {"baz", "qux"}, + } + + labels, err := tupleLablesToLabels(in) + require.NoError(t, err) + require.Equal(t, InstanceLabels{ + "foo": "bar", + "baz": "qux", + }, labels) + }) + + t.Run("nil input gives empty output", func(t *testing.T) { + labels, err := tupleLablesToLabels(nil) + require.NoError(t, err) + require.Empty(t, labels) + }) + + t.Run("duplicate keys are not allowed", func(t *testing.T) { + in := tupleLabels{ + {"foo", "bar"}, + {"foo", "qux"}, + } + + _, err := tupleLablesToLabels(in) + require.Error(t, err) + }) +} + +func BenchmarkTupleLabelsToLabels(b *testing.B) { + b.Run("10 labels", func(b *testing.B) { + in := make(tupleLabels, 0, 10) + for i := 0; i < 10; i++ { + key := fmt.Sprintf("key%d", i) + value := fmt.Sprintf("value%d", i) + in = append(in, tupleLabel{key, value}) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _, err := tupleLablesToLabels(in) + if err != nil { + b.Fatal(err) + } + } + }) + + b.Run("100 labels", func(b *testing.B) { + in := make(tupleLabels, 0, 100) + for i := 0; i < 100; i++ { + key := fmt.Sprintf("key%d", i) + value := fmt.Sprintf("value%d", i) + in = append(in, tupleLabel{key, value}) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _, err := tupleLablesToLabels(in) + if err != nil { + b.Fatal(err) + } + } + }) + + b.Run("10_000 labels", func(b *testing.B) { + in := make(tupleLabels, 0, 10_000) + for i := 0; i < 10_000; i++ { + key := fmt.Sprintf("key%d", i) + value := fmt.Sprintf("value%d", i) + in = append(in, tupleLabel{key, value}) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _, err := tupleLablesToLabels(in) + if err != nil { + b.Fatal(err) + } + } + }) + + b.Run("1_000_000 labels", func(b *testing.B) { + in := make(tupleLabels, 0, 1_000_000) + for i := 0; i < 1_000_000; i++ { + key := fmt.Sprintf("key%d", i) + value := fmt.Sprintf("value%d", i) + in = append(in, tupleLabel{key, value}) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _, err := tupleLablesToLabels(in) + if err != nil { + b.Fatal(err) + } + } + }) +}