prometheus/rules/origin_test.go
Julien Duchesne 1a27ab29b8
Some checks are pending
buf.build / lint and publish (push) Waiting to run
CI / Go tests (push) Waiting to run
CI / More Go tests (push) Waiting to run
CI / Go tests with previous Go version (push) Waiting to run
CI / UI tests (push) Waiting to run
CI / Go tests on Windows (push) Waiting to run
CI / Mixins tests (push) Waiting to run
CI / Build Prometheus for common architectures (0) (push) Waiting to run
CI / Build Prometheus for common architectures (1) (push) Waiting to run
CI / Build Prometheus for common architectures (2) (push) Waiting to run
CI / Build Prometheus for all architectures (0) (push) Waiting to run
CI / Build Prometheus for all architectures (1) (push) Waiting to run
CI / Build Prometheus for all architectures (10) (push) Waiting to run
CI / Build Prometheus for all architectures (11) (push) Waiting to run
CI / Build Prometheus for all architectures (2) (push) Waiting to run
CI / Build Prometheus for all architectures (3) (push) Waiting to run
CI / Build Prometheus for all architectures (4) (push) Waiting to run
CI / Build Prometheus for all architectures (5) (push) Waiting to run
CI / Build Prometheus for all architectures (6) (push) Waiting to run
CI / Build Prometheus for all architectures (7) (push) Waiting to run
CI / Build Prometheus for all architectures (8) (push) Waiting to run
CI / Build Prometheus for all architectures (9) (push) Waiting to run
CI / Report status of build Prometheus for all architectures (push) Blocked by required conditions
CI / Check generated parser (push) Waiting to run
CI / golangci-lint (push) Waiting to run
CI / fuzzing (push) Waiting to run
CI / codeql (push) Waiting to run
CI / Publish main branch artifacts (push) Blocked by required conditions
CI / Publish release artefacts (push) Blocked by required conditions
CI / Publish UI on npm Registry (push) Blocked by required conditions
Scorecards supply-chain security / Scorecards analysis (push) Waiting to run
Rules: Store dependencies instead of boolean (#15689)
* Rules: Store dependencies instead of boolean
To improve https://github.com/prometheus/prometheus/pull/15681 further, we'll need to store the dependencies and dependents of each

Right now, if a rule has both (at least 1) dependents and dependencies, it is not possible to determine the order to run the rules and they must all run sequentially

This PR only changes the dependents and dependencies attributes of rules, it does not implement a new topological sort algorithm

Signed-off-by: Julien Duchesne <julien.duchesne@grafana.com>

* Store a slice of Rule instead

Signed-off-by: Julien Duchesne <julien.duchesne@grafana.com>

* Add `BenchmarkRuleDependencyController_AnalyseRules` for future reference

Signed-off-by: Julien Duchesne <julien.duchesne@grafana.com>

---------

Signed-off-by: Julien Duchesne <julien.duchesne@grafana.com>
2025-01-06 20:48:38 +00:00

120 lines
4.3 KiB
Go

// Copyright 2023 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rules
import (
"context"
"net/url"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/prometheus/common/promslog"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/promql/parser"
)
type unknownRule struct{}
func (u unknownRule) Name() string { return "" }
func (u unknownRule) Labels() labels.Labels { return labels.EmptyLabels() }
func (u unknownRule) Eval(context.Context, time.Duration, time.Time, QueryFunc, *url.URL, int) (promql.Vector, error) {
return nil, nil
}
func (u unknownRule) String() string { return "" }
func (u unknownRule) Query() parser.Expr { return nil }
func (u unknownRule) SetLastError(error) {}
func (u unknownRule) LastError() error { return nil }
func (u unknownRule) SetHealth(RuleHealth) {}
func (u unknownRule) Health() RuleHealth { return "" }
func (u unknownRule) SetEvaluationDuration(time.Duration) {}
func (u unknownRule) GetEvaluationDuration() time.Duration { return 0 }
func (u unknownRule) SetEvaluationTimestamp(time.Time) {}
func (u unknownRule) GetEvaluationTimestamp() time.Time { return time.Time{} }
func (u unknownRule) SetDependentRules([]Rule) {}
func (u unknownRule) NoDependentRules() bool { return false }
func (u unknownRule) DependentRules() []Rule { return nil }
func (u unknownRule) SetDependencyRules([]Rule) {}
func (u unknownRule) NoDependencyRules() bool { return false }
func (u unknownRule) DependencyRules() []Rule { return nil }
func TestNewRuleDetailPanics(t *testing.T) {
require.PanicsWithValue(t, `unknown rule type "rules.unknownRule"`, func() {
NewRuleDetail(unknownRule{})
})
}
func TestFromOriginContext(t *testing.T) {
t.Run("should return zero value if RuleDetail is missing in the context", func(t *testing.T) {
detail := FromOriginContext(context.Background())
require.Zero(t, detail)
// The zero value for NoDependentRules must be the most conservative option.
require.False(t, detail.NoDependentRules)
// The zero value for NoDependencyRules must be the most conservative option.
require.False(t, detail.NoDependencyRules)
})
}
func TestNewRuleDetail(t *testing.T) {
t.Run("should populate NoDependentRules and NoDependencyRules for a RecordingRule", func(t *testing.T) {
rule := NewRecordingRule("test", &parser.NumberLiteral{Val: 1}, labels.EmptyLabels())
detail := NewRuleDetail(rule)
require.False(t, detail.NoDependentRules)
require.False(t, detail.NoDependencyRules)
rule.SetDependentRules([]Rule{})
detail = NewRuleDetail(rule)
require.True(t, detail.NoDependentRules)
require.False(t, detail.NoDependencyRules)
rule.SetDependencyRules([]Rule{})
detail = NewRuleDetail(rule)
require.True(t, detail.NoDependentRules)
require.True(t, detail.NoDependencyRules)
})
t.Run("should populate NoDependentRules and NoDependencyRules for a AlertingRule", func(t *testing.T) {
rule := NewAlertingRule(
"test",
&parser.NumberLiteral{Val: 1},
time.Minute,
0,
labels.FromStrings("test", "test"),
labels.EmptyLabels(),
labels.EmptyLabels(),
"",
true, promslog.NewNopLogger(),
)
detail := NewRuleDetail(rule)
require.False(t, detail.NoDependentRules)
require.False(t, detail.NoDependencyRules)
rule.SetDependentRules([]Rule{})
detail = NewRuleDetail(rule)
require.True(t, detail.NoDependentRules)
require.False(t, detail.NoDependencyRules)
rule.SetDependencyRules([]Rule{})
detail = NewRuleDetail(rule)
require.True(t, detail.NoDependentRules)
require.True(t, detail.NoDependencyRules)
})
}