mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
Ruler Concurrency: Consider __name__
matchers (#16039)
When calculating dependencies between rules, we sometimes run into `{__name__...}` matchers These can be used the same way as the actual rule names This will enable even more rules to run concurrently The new logic is also not slower: ``` julienduchesne@triceratops prometheus % benchstat test-old.txt test.txt goos: darwin goarch: arm64 pkg: github.com/prometheus/prometheus/rules cpu: Apple M3 Pro │ test-old.txt │ test.txt │ │ sec/op │ sec/op vs base │ DependencyMap-11 1.206µ ± 7% 1.024µ ± 7% -15.10% (p=0.000 n=10) │ test-old.txt │ test.txt │ │ B/op │ B/op vs base │ DependencyMap-11 1.720Ki ± 0% 1.438Ki ± 0% -16.35% (p=0.000 n=10) │ test-old.txt │ test.txt │ │ allocs/op │ allocs/op vs base │ DependencyMap-11 39.00 ± 0% 34.00 ± 0% -12.82% (p=0.000 n=10) ``` Signed-off-by: Julien Duchesne <julien.duchesne@grafana.com>
This commit is contained in:
parent
a5ffa83be8
commit
77a5698190
|
@ -1110,9 +1110,6 @@ func buildDependencyMap(rules []Rule) dependencyMap {
|
|||
return dependencies
|
||||
}
|
||||
|
||||
inputs := make(map[string][]Rule, len(rules))
|
||||
outputs := make(map[string][]Rule, len(rules))
|
||||
|
||||
var indeterminate bool
|
||||
|
||||
for _, rule := range rules {
|
||||
|
@ -1120,26 +1117,46 @@ func buildDependencyMap(rules []Rule) dependencyMap {
|
|||
break
|
||||
}
|
||||
|
||||
name := rule.Name()
|
||||
outputs[name] = append(outputs[name], rule)
|
||||
|
||||
parser.Inspect(rule.Query(), func(node parser.Node, path []parser.Node) error {
|
||||
if n, ok := node.(*parser.VectorSelector); ok {
|
||||
// Find the name matcher for the rule.
|
||||
var nameMatcher *labels.Matcher
|
||||
if n.Name != "" {
|
||||
nameMatcher = labels.MustNewMatcher(labels.MatchEqual, model.MetricNameLabel, n.Name)
|
||||
} else {
|
||||
for _, m := range n.LabelMatchers {
|
||||
if m.Name == model.MetricNameLabel {
|
||||
nameMatcher = m
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A wildcard metric expression means we cannot reliably determine if this rule depends on any other,
|
||||
// which means we cannot safely run any rules concurrently.
|
||||
if n.Name == "" && len(n.LabelMatchers) > 0 {
|
||||
if nameMatcher == nil {
|
||||
indeterminate = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Rules which depend on "meta-metrics" like ALERTS and ALERTS_FOR_STATE will have undefined behaviour
|
||||
// if they run concurrently.
|
||||
if n.Name == alertMetricName || n.Name == alertForStateMetricName {
|
||||
if nameMatcher.Matches(alertMetricName) || nameMatcher.Matches(alertForStateMetricName) {
|
||||
indeterminate = true
|
||||
return nil
|
||||
}
|
||||
|
||||
inputs[n.Name] = append(inputs[n.Name], rule)
|
||||
// Find rules which depend on the output of this rule.
|
||||
for _, other := range rules {
|
||||
if other == rule {
|
||||
continue
|
||||
}
|
||||
|
||||
otherName := other.Name()
|
||||
if nameMatcher.Matches(otherName) {
|
||||
dependencies[other] = append(dependencies[other], rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
@ -1149,13 +1166,5 @@ func buildDependencyMap(rules []Rule) dependencyMap {
|
|||
return nil
|
||||
}
|
||||
|
||||
for output, outRules := range outputs {
|
||||
for _, outRule := range outRules {
|
||||
if inRules, found := inputs[output]; found && len(inRules) > 0 {
|
||||
dependencies[outRule] = append(dependencies[outRule], inRules...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dependencies
|
||||
}
|
||||
|
|
|
@ -1601,10 +1601,14 @@ func TestDependencyMap(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
rule4 := NewRecordingRule("user:requests:increase1h", expr, labels.Labels{})
|
||||
|
||||
expr, err = parser.ParseExpr(`sum by (user) ({__name__=~"user:requests.+5m"})`)
|
||||
require.NoError(t, err)
|
||||
rule5 := NewRecordingRule("user:requests:sum5m", expr, labels.Labels{})
|
||||
|
||||
group := NewGroup(GroupOptions{
|
||||
Name: "rule_group",
|
||||
Interval: time.Second,
|
||||
Rules: []Rule{rule, rule2, rule3, rule4},
|
||||
Rules: []Rule{rule, rule2, rule3, rule4, rule5},
|
||||
Opts: opts,
|
||||
})
|
||||
|
||||
|
@ -1619,13 +1623,17 @@ func TestDependencyMap(t *testing.T) {
|
|||
require.Equal(t, []Rule{rule}, depMap.dependencies(rule2))
|
||||
require.False(t, depMap.isIndependent(rule2))
|
||||
|
||||
require.Zero(t, depMap.dependents(rule3))
|
||||
require.Equal(t, []Rule{rule5}, depMap.dependents(rule3))
|
||||
require.Zero(t, depMap.dependencies(rule3))
|
||||
require.True(t, depMap.isIndependent(rule3))
|
||||
require.False(t, depMap.isIndependent(rule3))
|
||||
|
||||
require.Zero(t, depMap.dependents(rule4))
|
||||
require.Equal(t, []Rule{rule}, depMap.dependencies(rule4))
|
||||
require.False(t, depMap.isIndependent(rule4))
|
||||
|
||||
require.Zero(t, depMap.dependents(rule5))
|
||||
require.Equal(t, []Rule{rule3}, depMap.dependencies(rule5))
|
||||
require.False(t, depMap.isIndependent(rule5))
|
||||
}
|
||||
|
||||
func TestNoDependency(t *testing.T) {
|
||||
|
|
Loading…
Reference in a new issue