Skip to content

Commit 4eb03df

Browse files
committed
Add support for prometheus rule query offset
Signed-off-by: Mustafain Ali Khan <[email protected]>
1 parent 505cdc2 commit 4eb03df

File tree

8 files changed

+212
-71
lines changed

8 files changed

+212
-71
lines changed

pkg/ruler/compat.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,9 @@ func DefaultTenantManagerFactory(cfg Config, p Pusher, q storage.Queryable, engi
358358
ResendDelay: cfg.ResendDelay,
359359
ConcurrentEvalsEnabled: cfg.ConcurrentEvalsEnabled,
360360
MaxConcurrentEvals: cfg.MaxConcurrentEvals,
361+
DefaultRuleQueryOffset: func() time.Duration {
362+
return cfg.RuleQueryOffset
363+
},
361364
})
362365
}
363366
}

pkg/ruler/ruler.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ type Config struct {
121121
// Client configs for interacting with the Alertmanager
122122
Notifier NotifierConfig `yaml:"alertmanager_client"`
123123

124+
//Default offset for all rule evaluation queries.
125+
RuleQueryOffset time.Duration `yaml:"rule_query_offset"`
124126
// Max time to tolerate outage for restoring "for" state of alert.
125127
OutageTolerance time.Duration `yaml:"for_outage_tolerance"`
126128
// Minimum duration between alert and restored "for" state. This is maintained only for alerts with configured "for" time greater than grace period.
@@ -194,6 +196,7 @@ func (cfg *Config) RegisterFlags(f *flag.FlagSet) {
194196
f.Var(&cfg.ExternalURL, "ruler.external.url", "URL of alerts return path.")
195197
f.DurationVar(&cfg.EvaluationInterval, "ruler.evaluation-interval", 1*time.Minute, "How frequently to evaluate rules")
196198
f.DurationVar(&cfg.PollInterval, "ruler.poll-interval", 1*time.Minute, "How frequently to poll for rule changes")
199+
f.DurationVar(&cfg.RuleQueryOffset, "ruler.rule-query-offset", 0*time.Minute, "Default offset for all rule evaluation queries")
197200

198201
f.StringVar(&cfg.AlertmanagerURL, "ruler.alertmanager-url", "", "Comma-separated list of URL(s) of the Alertmanager(s) to send notifications to. Each Alertmanager URL is treated as a separate group in the configuration. Multiple Alertmanagers in HA per group can be supported by using DNS resolution via -ruler.alertmanager-discovery.")
199202
f.BoolVar(&cfg.AlertmanagerDiscovery, "ruler.alertmanager-discovery", false, "Use DNS SRV records to discover Alertmanager hosts.")
@@ -909,11 +912,12 @@ func (r *Ruler) getLocalRules(userID string, rulesRequest RulesRequest, includeB
909912

910913
groupDesc := &GroupStateDesc{
911914
Group: &rulespb.RuleGroupDesc{
912-
Name: group.Name(),
913-
Namespace: string(decodedNamespace),
914-
Interval: interval,
915-
User: userID,
916-
Limit: int64(group.Limit()),
915+
Name: group.Name(),
916+
Namespace: string(decodedNamespace),
917+
Interval: interval,
918+
User: userID,
919+
Limit: int64(group.Limit()),
920+
QueryOffset: group.QueryOffset(),
917921
},
918922

919923
EvaluationTimestamp: group.GetLastEvaluation(),

pkg/ruler/ruler_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2397,3 +2397,34 @@ func TestRulerDisablesRuleGroups(t *testing.T) {
23972397
})
23982398
}
23992399
}
2400+
2401+
func TestRuler_QueryOffset(t *testing.T) {
2402+
store := newMockRuleStore(mockRulesQueryOffset, nil)
2403+
cfg := defaultRulerConfig(t)
2404+
2405+
r := newTestRuler(t, cfg, store, nil)
2406+
defer services.StopAndAwaitTerminated(context.Background(), r) //nolint:errcheck
2407+
2408+
ctx := user.InjectOrgID(context.Background(), "user1")
2409+
rls, err := r.Rules(ctx, &RulesRequest{})
2410+
require.NoError(t, err)
2411+
require.Len(t, rls.Groups, 1)
2412+
rg := rls.Groups[0]
2413+
expectedRg := mockRulesQueryOffset["user1"][0]
2414+
compareRuleGroupDescToStateDesc(t, expectedRg, rg)
2415+
2416+
// test default query offset=0
2417+
require.Equal(t, time.Duration(0), rg.GetGroup().QueryOffset)
2418+
2419+
ctx = user.InjectOrgID(context.Background(), "user2")
2420+
rls, err = r.Rules(ctx, &RulesRequest{})
2421+
require.NoError(t, err)
2422+
require.Len(t, rls.Groups, 1)
2423+
rg = rls.Groups[0]
2424+
expectedRg = mockRules["user2"][0]
2425+
compareRuleGroupDescToStateDesc(t, expectedRg, rg)
2426+
2427+
// test group query offset is set
2428+
require.Equal(t, time.Minute*2, rg.GetGroup().QueryOffset)
2429+
rulespb.FromProto(rg.GetGroup())
2430+
}

pkg/ruler/rulespb/compat.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ import (
1414
// ToProto transforms a formatted prometheus rulegroup to a rule group protobuf
1515
func ToProto(user string, namespace string, rl rulefmt.RuleGroup) *RuleGroupDesc {
1616
rg := RuleGroupDesc{
17-
Name: rl.Name,
18-
Namespace: namespace,
19-
Interval: time.Duration(rl.Interval),
20-
Rules: formattedRuleToProto(rl.Rules),
21-
User: user,
22-
Limit: int64(rl.Limit),
17+
Name: rl.Name,
18+
Namespace: namespace,
19+
Interval: time.Duration(rl.Interval),
20+
Rules: formattedRuleToProto(rl.Rules),
21+
User: user,
22+
Limit: int64(rl.Limit),
23+
QueryOffset: time.Duration(*rl.QueryOffset),
2324
}
2425
return &rg
2526
}
@@ -43,11 +44,13 @@ func formattedRuleToProto(rls []rulefmt.RuleNode) []*RuleDesc {
4344

4445
// FromProto generates a rulefmt RuleGroup
4546
func FromProto(rg *RuleGroupDesc) rulefmt.RuleGroup {
47+
queryOffset := model.Duration(rg.QueryOffset)
4648
formattedRuleGroup := rulefmt.RuleGroup{
47-
Name: rg.GetName(),
48-
Interval: model.Duration(rg.Interval),
49-
Rules: make([]rulefmt.RuleNode, len(rg.GetRules())),
50-
Limit: int(rg.Limit),
49+
Name: rg.GetName(),
50+
Interval: model.Duration(rg.Interval),
51+
Rules: make([]rulefmt.RuleNode, len(rg.GetRules())),
52+
Limit: int(rg.Limit),
53+
QueryOffset: &queryOffset,
5154
}
5255

5356
for i, rl := range rg.GetRules() {

pkg/ruler/rulespb/compat_test.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,18 @@ func TestProto(t *testing.T) {
2929

3030
rules = append(rules, testRule)
3131

32+
queryOffset := model.Duration(30 * time.Second)
3233
rg := rulefmt.RuleGroup{
33-
Name: "group1",
34-
Rules: rules,
35-
Interval: model.Duration(time.Minute),
34+
Name: "group1",
35+
Rules: rules,
36+
Interval: model.Duration(time.Minute),
37+
QueryOffset: &queryOffset,
3638
}
39+
3740
desc := ToProto("test", "namespace", rg)
3841

3942
assert.Equal(t, len(rules), len(desc.Rules))
43+
assert.Equal(t, 30*time.Second, desc.QueryOffset)
4044

4145
ruleDesc := desc.Rules[0]
4246

pkg/ruler/rulespb/rules.pb.go

Lines changed: 110 additions & 52 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)