Skip to content

Commit f41fccb

Browse files
Inhibition Rules: Add benchmarks for Mutes (#3772)
* Add benchmarks for Mutes This commit adds benchmarks for Mutes in the inhibit package. Signed-off-by: George Robinson <[email protected]> --------- Signed-off-by: George Robinson <[email protected]>
1 parent 6c70b5c commit f41fccb

File tree

1 file changed

+180
-0
lines changed

1 file changed

+180
-0
lines changed

inhibit/inhibit_bench_test.go

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
// Copyright 2024 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package inhibit
15+
16+
import (
17+
"context"
18+
"errors"
19+
"strconv"
20+
"testing"
21+
"time"
22+
23+
"github.com/go-kit/log"
24+
"github.com/prometheus/client_golang/prometheus"
25+
"github.com/prometheus/common/model"
26+
"github.com/stretchr/testify/require"
27+
28+
"github.com/prometheus/alertmanager/config"
29+
"github.com/prometheus/alertmanager/pkg/labels"
30+
"github.com/prometheus/alertmanager/provider/mem"
31+
"github.com/prometheus/alertmanager/types"
32+
)
33+
34+
// BenchmarkMutes benchmarks the Mutes method for the Muter interface
35+
// for different numbers of inhibition rules.
36+
func BenchmarkMutes(b *testing.B) {
37+
b.Run("1 inhibition rule, 1 inhibiting alert", func(b *testing.B) {
38+
benchmarkMutes(b, defaultBenchmark(b, 1, 1))
39+
})
40+
b.Run("10 inhibition rules, 1 inhibiting alert", func(b *testing.B) {
41+
benchmarkMutes(b, defaultBenchmark(b, 10, 1))
42+
})
43+
b.Run("100 inhibition rules, 1 inhibiting alert", func(b *testing.B) {
44+
benchmarkMutes(b, defaultBenchmark(b, 100, 1))
45+
})
46+
b.Run("1000 inhibition rules, 1 inhibiting alert", func(b *testing.B) {
47+
benchmarkMutes(b, defaultBenchmark(b, 1000, 1))
48+
})
49+
b.Run("10000 inhibition rules, 1 inhibiting alert", func(b *testing.B) {
50+
benchmarkMutes(b, defaultBenchmark(b, 10000, 1))
51+
})
52+
b.Run("1 inhibition rule, 10 inhibiting alerts", func(b *testing.B) {
53+
benchmarkMutes(b, defaultBenchmark(b, 1, 10))
54+
})
55+
b.Run("1 inhibition rule, 100 inhibiting alerts", func(b *testing.B) {
56+
benchmarkMutes(b, defaultBenchmark(b, 1, 100))
57+
})
58+
b.Run("1 inhibition rule, 1000 inhibiting alerts", func(b *testing.B) {
59+
benchmarkMutes(b, defaultBenchmark(b, 1, 1000))
60+
})
61+
b.Run("1 inhibition rule, 10000 inhibiting alerts", func(b *testing.B) {
62+
benchmarkMutes(b, defaultBenchmark(b, 1, 10000))
63+
})
64+
b.Run("100 inhibition rules, 1000 inhibiting alerts", func(b *testing.B) {
65+
benchmarkMutes(b, defaultBenchmark(b, 100, 1000))
66+
})
67+
}
68+
69+
// benchmarkOptions allows the declaration of a wide range of benchmarks.
70+
type benchmarkOptions struct {
71+
// n is the total number of inhibition rules.
72+
n int
73+
// newRuleFunc creates the next inhibition rule. It is called n times.
74+
newRuleFunc func(idx int) config.InhibitRule
75+
// newAlertsFunc creates the inhibiting alerts for each inhibition rule.
76+
// It is called n times.
77+
newAlertsFunc func(idx int, r config.InhibitRule) []types.Alert
78+
// benchFunc runs the benchmark.
79+
benchFunc func(mutesFunc func(model.LabelSet) bool) error
80+
}
81+
82+
// defaultBenchmark returns the default benchmark. It supports a number of
83+
// variations, including customization of the number of inhibition rules,
84+
// and the number of inhibiting alerts per inhibition rule.
85+
//
86+
// The source matchers are suffixed with the position of the inhibition rule
87+
// in the list. For example, src=1, src=2, etc. The target matchers are
88+
// the same across all inhibition rules (dst=0).
89+
//
90+
// Each inhibition rule can have zero or more alerts that match the source
91+
// matchers, and is determined with numInhibitingAlerts.
92+
//
93+
// The default benchmark expects dst=0 to be muted and will fail if not.
94+
func defaultBenchmark(b *testing.B, numInhibitionRules, numInhibitingAlerts int) benchmarkOptions {
95+
return benchmarkOptions{
96+
n: numInhibitionRules,
97+
newRuleFunc: func(idx int) config.InhibitRule {
98+
return config.InhibitRule{
99+
SourceMatchers: config.Matchers{
100+
mustNewMatcher(b, labels.MatchEqual, "src", strconv.Itoa(idx)),
101+
},
102+
TargetMatchers: config.Matchers{
103+
mustNewMatcher(b, labels.MatchEqual, "dst", "0"),
104+
},
105+
}
106+
},
107+
newAlertsFunc: func(idx int, _ config.InhibitRule) []types.Alert {
108+
var alerts []types.Alert
109+
for i := 0; i < numInhibitingAlerts; i++ {
110+
alerts = append(alerts, types.Alert{
111+
Alert: model.Alert{
112+
Labels: model.LabelSet{
113+
"src": model.LabelValue(strconv.Itoa(idx)),
114+
"idx": model.LabelValue(strconv.Itoa(i)),
115+
},
116+
},
117+
})
118+
}
119+
return alerts
120+
}, benchFunc: func(mutesFunc func(set model.LabelSet) bool) error {
121+
if ok := mutesFunc(model.LabelSet{"dst": "0"}); !ok {
122+
return errors.New("expected dst=0 to be muted")
123+
}
124+
return nil
125+
},
126+
}
127+
}
128+
129+
func benchmarkMutes(b *testing.B, opts benchmarkOptions) {
130+
r := prometheus.NewRegistry()
131+
m := types.NewMarker(r)
132+
s, err := mem.NewAlerts(context.TODO(), m, time.Minute, nil, log.NewNopLogger(), r)
133+
if err != nil {
134+
b.Fatal(err)
135+
}
136+
defer s.Close()
137+
138+
alerts, rules := benchmarkFromOptions(opts)
139+
for _, a := range alerts {
140+
tmp := a
141+
if err = s.Put(&tmp); err != nil {
142+
b.Fatal(err)
143+
}
144+
}
145+
146+
ih := NewInhibitor(s, rules, m, log.NewNopLogger())
147+
defer ih.Stop()
148+
go ih.Run()
149+
150+
// Wait some time for the inhibitor to seed its cache.
151+
waitDuration := time.Millisecond * time.Duration(len(alerts))
152+
if waitDuration > time.Second {
153+
waitDuration = time.Second
154+
}
155+
<-time.After(waitDuration)
156+
b.ResetTimer()
157+
158+
for i := 0; i < b.N; i++ {
159+
require.NoError(b, opts.benchFunc(ih.Mutes))
160+
}
161+
}
162+
163+
func benchmarkFromOptions(opts benchmarkOptions) ([]types.Alert, []config.InhibitRule) {
164+
var (
165+
alerts = make([]types.Alert, 0, opts.n)
166+
rules = make([]config.InhibitRule, 0, opts.n)
167+
)
168+
for i := 0; i < opts.n; i++ {
169+
r := opts.newRuleFunc(i)
170+
alerts = append(alerts, opts.newAlertsFunc(i, r)...)
171+
rules = append(rules, r)
172+
}
173+
return alerts, rules
174+
}
175+
176+
func mustNewMatcher(b *testing.B, op labels.MatchType, name, value string) *labels.Matcher {
177+
m, err := labels.NewMatcher(op, name, value)
178+
require.NoError(b, err)
179+
return m
180+
}

0 commit comments

Comments
 (0)