Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion inhibit/inhibit.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,24 @@ func (r *InhibitRule) findEqualSourceAlert(lset model.LabelSet, now time.Time) (
return nil, false
}

func (r *InhibitRule) findEqualSourceAlertFromCache(lset model.LabelSet, excludeTwoSidedMatch bool, now time.Time) (*types.Alert, bool) {
equalsFP := r.fingerprintEquals(lset)
for _, alert := range r.scache.List() {
if alert.ResolvedAt(now) {
continue
}
if r.fingerprintEquals(alert.Labels) != equalsFP {
continue
}
if excludeTwoSidedMatch && r.TargetMatchers.Matches(alert.Labels) {
continue
}
return alert, true
}

return nil, false
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This scans the whole cache whenever the indexed source is two-sided which is O(number of source alerts), allocates a copy, locks the shared cache mutex, etc.
This is basically skipping the optimisations #4607 introduced.
Also the current benchmark matrix does not cover this case.


func (r *InhibitRule) gcCallback(alerts []*types.Alert) {
for _, a := range alerts {
fp := r.fingerprintEquals(a.Labels)
Expand All @@ -412,7 +430,11 @@ func (r *InhibitRule) hasEqual(lset model.LabelSet, excludeTwoSidedMatch bool, n
equal, found := r.findEqualSourceAlert(lset, now)
if found {
if excludeTwoSidedMatch && r.TargetMatchers.Matches(equal.Labels) {
return model.Fingerprint(0), false
equal, found = r.findEqualSourceAlertFromCache(lset, excludeTwoSidedMatch, now)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new method is only called when a source alert is found in cache initially but is disqualified.
The index can be absent while same-equal active sources remain.
gcCallback deletes the whole equal-label key when any alert in that bucket is collected.
So we need a regression test where GC removes a non-indexed same-equal alert while a source-only alert remains active. Ideally the index should remove a specific source fingerprint rather than deleting the bucket.

if !found {
return model.Fingerprint(0), false
}
return equal.Fingerprint(), true
}
return equal.Fingerprint(), found
}
Expand Down
47 changes: 38 additions & 9 deletions inhibit/inhibit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,13 @@ func TestInhibitRuleHasEqual(t *testing.T) {

now := time.Now()
cases := []struct {
name string
initial map[model.Fingerprint]*alert.Alert
equal model.LabelNames
input model.LabelSet
result bool
name string
initial map[model.Fingerprint]*alert.Alert
equal model.LabelNames
targetMatchers labels.Matchers
input model.LabelSet
excludeTwoSidedMatch bool
result bool
}{
{
name: "no source alerts",
Expand Down Expand Up @@ -141,14 +143,41 @@ func TestInhibitRuleHasEqual(t *testing.T) {
input: model.LabelSet{"a": "b"},
result: false,
},
{
name: "matching source-only alert still inhibits when newest equal source is two-sided",
initial: map[model.Fingerprint]*alert.Alert{
1: {
Alert: model.Alert{
Labels: model.LabelSet{"s": "1", "e": "1"},
StartsAt: now.Add(-time.Minute),
EndsAt: now.Add(time.Hour),
},
},
2: {
Alert: model.Alert{
Labels: model.LabelSet{"s": "1", "t": "1", "e": "1"},
StartsAt: now.Add(-time.Minute),
EndsAt: now.Add(2 * time.Hour),
},
},
},
equal: model.LabelNames{"e"},
targetMatchers: labels.Matchers{{Type: labels.MatchEqual, Name: "t", Value: "1"}},
input: model.LabelSet{"s": "1", "t": "1", "e": "1"},
// The indexed two-sided source must be ignored, but the source-only
// alert with the same equal labels should still inhibit the target.
excludeTwoSidedMatch: true,
result: true,
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
r := &InhibitRule{
Equal: map[model.LabelName]struct{}{},
scache: store.NewAlerts(),
sindex: newIndex(),
Equal: map[model.LabelName]struct{}{},
TargetMatchers: c.targetMatchers,
scache: store.NewAlerts(),
sindex: newIndex(),
}
for _, ln := range c.equal {
r.Equal[ln] = struct{}{}
Expand All @@ -158,7 +187,7 @@ func TestInhibitRuleHasEqual(t *testing.T) {
r.updateIndex(v)
}

if _, have := r.hasEqual(c.input, false, time.Now()); have != c.result {
if _, have := r.hasEqual(c.input, c.excludeTwoSidedMatch, time.Now()); have != c.result {
t.Errorf("Unexpected result %t, expected %t", have, c.result)
}
})
Expand Down
Loading