Skip to content

Commit cb2db88

Browse files
committed
Fix rule group query offset issue
This is a follow-up to cortexproject#6085, which added support for setting the `query_offset` field on individual recording rule groups, as well as a per-tenant `ruler_query_offset` limit that should be used when no individual recording rule group offset is set. It turns out that compatibility code to convert from a protobuf RuleGroup to a prometheus RuleGroup was coercing null-value query offsets to explicit 0s, which meant that no rule groups would ever fall back to the per-tenant offset. This PR fixes that issue, and it cleans up handling of the query offset in a few other ruler files. Signed-off-by: Kevin Ingelman <[email protected]>
1 parent 85bc555 commit cb2db88

File tree

4 files changed

+28
-13
lines changed

4 files changed

+28
-13
lines changed

pkg/ruler/api.go

+7
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ type RuleGroup struct {
6262
// same array.
6363
Rules []rule `json:"rules"`
6464
Interval float64 `json:"interval"`
65+
QueryOffset *float64 `json:"queryOffset,omitempty"`
6566
LastEvaluation time.Time `json:"lastEvaluation"`
6667
EvaluationTime float64 `json:"evaluationTime"`
6768
Limit int64 `json:"limit"`
@@ -182,11 +183,17 @@ func (a *API) PrometheusRules(w http.ResponseWriter, req *http.Request) {
182183
groups := make([]*RuleGroup, 0, len(rgs))
183184

184185
for _, g := range rgs {
186+
var queryOffset *float64
187+
if g.Group.QueryOffset != nil {
188+
offset := g.Group.QueryOffset.Seconds()
189+
queryOffset = &offset
190+
}
185191
grp := RuleGroup{
186192
Name: g.Group.Name,
187193
File: g.Group.Namespace,
188194
Rules: make([]rule, len(g.ActiveRules)),
189195
Interval: g.Group.Interval.Seconds(),
196+
QueryOffset: queryOffset,
190197
LastEvaluation: g.GetEvaluationTimestamp(),
191198
EvaluationTime: g.GetEvaluationDuration().Seconds(),
192199
Limit: g.Group.Limit,

pkg/ruler/api_test.go

+11-5
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ func TestRuler_rules(t *testing.T) {
4444
require.Equal(t, responseJSON.Status, "success")
4545

4646
// Testing the running rules for user1 in the mock store
47+
queryOffset := float64(0)
4748
expectedResponse, _ := json.Marshal(util_api.Response{
4849
Status: "success",
4950
Data: &RuleDiscovery{
@@ -67,7 +68,8 @@ func TestRuler_rules(t *testing.T) {
6768
Alerts: []*Alert{},
6869
},
6970
},
70-
Interval: 60,
71+
Interval: 60,
72+
QueryOffset: &queryOffset,
7173
},
7274
},
7375
},
@@ -100,6 +102,7 @@ func TestRuler_rules_special_characters(t *testing.T) {
100102
require.Equal(t, responseJSON.Status, "success")
101103

102104
// Testing the running rules for user1 in the mock store
105+
queryOffset := float64(0)
103106
expectedResponse, _ := json.Marshal(util_api.Response{
104107
Status: "success",
105108
Data: &RuleDiscovery{
@@ -123,7 +126,8 @@ func TestRuler_rules_special_characters(t *testing.T) {
123126
Alerts: []*Alert{},
124127
},
125128
},
126-
Interval: 60,
129+
Interval: 60,
130+
QueryOffset: &queryOffset,
127131
},
128132
},
129133
},
@@ -154,6 +158,7 @@ func TestRuler_rules_limit(t *testing.T) {
154158
require.Equal(t, responseJSON.Status, "success")
155159

156160
// Testing the running rules for user1 in the mock store
161+
queryOffset := float64(0)
157162
expectedResponse, _ := json.Marshal(util_api.Response{
158163
Status: "success",
159164
Data: &RuleDiscovery{
@@ -178,7 +183,8 @@ func TestRuler_rules_limit(t *testing.T) {
178183
Alerts: []*Alert{},
179184
},
180185
},
181-
Interval: 60,
186+
Interval: 60,
187+
QueryOffset: &queryOffset,
182188
},
183189
},
184190
},
@@ -279,7 +285,7 @@ rules:
279285
labels:
280286
test: test
281287
`,
282-
output: "name: test\ninterval: 15s\nquery_offset: 0s\nrules:\n - record: up_rule\n expr: up{}\n - alert: up_alert\n expr: sum(up{}) > 1\n for: 30s\n labels:\n test: test\n annotations:\n test: test\n",
288+
output: "name: test\ninterval: 15s\nrules:\n - record: up_rule\n expr: up{}\n - alert: up_alert\n expr: sum(up{}) > 1\n for: 30s\n labels:\n test: test\n annotations:\n test: test\n",
283289
},
284290
{
285291
name: "with a valid rule query offset",
@@ -342,7 +348,7 @@ func TestRuler_DeleteNamespace(t *testing.T) {
342348

343349
router.ServeHTTP(w, req)
344350
require.Equal(t, http.StatusOK, w.Code)
345-
require.Equal(t, "name: group1\ninterval: 1m\nquery_offset: 0s\nrules:\n - record: UP_RULE\n expr: up\n - alert: UP_ALERT\n expr: up < 1\n", w.Body.String())
351+
require.Equal(t, "name: group1\ninterval: 1m\nrules:\n - record: UP_RULE\n expr: up\n - alert: UP_ALERT\n expr: up < 1\n", w.Body.String())
346352

347353
// Delete namespace1
348354
req = requestFor(t, http.MethodDelete, "https://localhost:8080/api/v1/rules/namespace1", nil, "user1")

pkg/ruler/ruler.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -1061,11 +1061,12 @@ func (r *Ruler) ruleGroupListToGroupStateDesc(userID string, backupGroups rulesp
10611061

10621062
groupDesc := &GroupStateDesc{
10631063
Group: &rulespb.RuleGroupDesc{
1064-
Name: group.GetName(),
1065-
Namespace: group.GetNamespace(),
1066-
Interval: interval,
1067-
User: userID,
1068-
Limit: group.Limit,
1064+
Name: group.GetName(),
1065+
Namespace: group.GetNamespace(),
1066+
Interval: interval,
1067+
User: userID,
1068+
Limit: group.Limit,
1069+
QueryOffset: group.QueryOffset,
10691070
},
10701071
// We are keeping default value for EvaluationTimestamp and EvaluationDuration since the backup is not evaluating
10711072
}

pkg/ruler/rulespb/compat.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,17 @@ func formattedRuleToProto(rls []rulefmt.RuleNode) []*RuleDesc {
4949

5050
// FromProto generates a rulefmt RuleGroup
5151
func FromProto(rg *RuleGroupDesc) rulefmt.RuleGroup {
52-
var queryOffset model.Duration
52+
var queryOffset *model.Duration
5353
if rg.QueryOffset != nil {
54-
queryOffset = model.Duration(*rg.QueryOffset)
54+
offset := model.Duration(*rg.QueryOffset)
55+
queryOffset = &offset
5556
}
5657
formattedRuleGroup := rulefmt.RuleGroup{
5758
Name: rg.GetName(),
5859
Interval: model.Duration(rg.Interval),
5960
Rules: make([]rulefmt.RuleNode, len(rg.GetRules())),
6061
Limit: int(rg.Limit),
61-
QueryOffset: &queryOffset,
62+
QueryOffset: queryOffset,
6263
}
6364

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

0 commit comments

Comments
 (0)