@@ -87,22 +87,25 @@ func (ih *Inhibitor) run(ctx context.Context) {
8787 // Update the inhibition rules' cache.
8888 cachedSum := 0
8989 indexedSum := 0
90+ cached := 0
91+ indexed := 0
9092 for _ , r := range ih .rules {
9193 if len (r .Sources ) > 0 {
92- allSrcMatched := true
94+ cached = 0
95+ indexed = 0
9396 for _ , src := range r .Sources {
94- if ! src .SrcMatchers .Matches (a .Labels ) {
95- allSrcMatched = false
97+ if src .SrcMatchers .Matches (a .Labels ) {
98+ if err := src .scache .Set (a ); err != nil {
99+ ih .logger .Error ("error on set alert" , "err" , err )
100+ continue
101+ }
102+ src .updateIndex (a )
103+ cached += src .scache .Len ()
104+ indexed += src .sindex .Len ()
96105 break
97106 }
98107 }
99- if allSrcMatched {
100- if err := r .scache .Set (a ); err != nil {
101- ih .logger .Error ("error on set alert" , "err" , err )
102- continue
103- }
104- r .updateIndex (a )
105- }
108+
106109 } else {
107110 if r .SourceMatchers .Matches (a .Labels ) {
108111 if err := r .scache .Set (a ); err != nil {
@@ -111,16 +114,14 @@ func (ih *Inhibitor) run(ctx context.Context) {
111114 }
112115 r .updateIndex (a )
113116 }
114- }
115-
116- cached := r .scache .Len ()
117- indexed := r .sindex .Len ()
117+ cached = r .scache .Len ()
118+ indexed = r .sindex .Len ()
118119
120+ }
119121 if r .Name != "" {
120122 r .metrics .sourceAlertsCacheItems .With (prometheus.Labels {"rule" : r .Name }).Set (float64 (cached ))
121123 r .metrics .sourceAlertsIndexItems .With (prometheus.Labels {"rule" : r .Name }).Set (float64 (indexed ))
122124 }
123-
124125 cachedSum += cached
125126 indexedSum += indexed
126127 }
@@ -189,17 +190,14 @@ func (ih *Inhibitor) Mutes(lset model.LabelSet) bool {
189190 // need to exclude inhibiting alerts for which the same is true.
190191
191192 if len (r .Sources ) > 0 {
192- allSourcesMatched := true
193193 var inhibitorIDs []string
194194 for _ , source := range r .Sources {
195- if inhibitedByFP , eq := r .hasEqual (lset , source .SrcMatchers .Matches (lset ), ruleStart ); eq {
195+ if inhibitedByFP , eq := source .hasEqual (lset , source .SrcMatchers .Matches (lset ), ruleStart , r . TargetMatchers ); eq && ! source . foundMatch {
196196 inhibitorIDs = append (inhibitorIDs , inhibitedByFP .String ())
197- } else {
198- allSourcesMatched = false
199- break
197+ source .foundMatch = true
200198 }
201199 }
202- if allSourcesMatched {
200+ if allSourcesMatched := r . allSourcesSatisfied (); allSourcesMatched {
203201 compositeInhibitorID := strings .Join (inhibitorIDs , "," )
204202 ih .marker .SetInhibited (fp , compositeInhibitorID )
205203 now := time .Now ()
@@ -209,6 +207,11 @@ func (ih *Inhibitor) Mutes(lset model.LabelSet) bool {
209207 r .metrics .mutesDurationMuted .Observe (sinceRuleStart .Seconds ())
210208 return true
211209 }
210+ // Reset for next use.
211+ for _ , source := range r .Sources {
212+ source .foundMatch = false
213+ }
214+
212215 } else {
213216 if inhibitedByFP , eq := r .hasEqual (lset , r .SourceMatchers .Matches (lset ), ruleStart ); eq {
214217 ih .marker .SetInhibited (fp , inhibitedByFP .String ())
@@ -237,6 +240,16 @@ type Source struct {
237240 // A set of label names whose label values need to be identical in source and
238241 // target alerts in order for the inhibition to take effect.
239242 Equal map [model.LabelName ]struct {}
243+ // Cache of alerts matching source labels.
244+ scache * store.Alerts
245+
246+ // Index of fingerprints of source alert equal labels to fingerprint of source alert.
247+ // The index helps speed up source alert lookups from scache significantely in scenarios with 100s of source alerts cached.
248+ // The index items might overwrite eachother if multiple source alerts have exact equal labels.
249+ // Overwrites only happen if the new source alert has bigger EndsAt value.
250+ sindex * index
251+
252+ foundMatch bool
240253}
241254
242255// An InhibitRule specifies that a class of (source) alerts should inhibit
@@ -250,7 +263,7 @@ type InhibitRule struct {
250263 // The set of Filters which define the group of source alerts (which inhibit
251264 // the target alerts).
252265 SourceMatchers labels.Matchers
253- Sources []Source
266+ Sources []* Source
254267 // The set of Filters which define the group of target alerts (which are
255268 // inhibited by the source alerts).
256269 TargetMatchers labels.Matchers
@@ -273,21 +286,24 @@ type InhibitRule struct {
273286// NewInhibitRule returns a new InhibitRule based on a configuration definition.
274287func NewInhibitRule (cr config.InhibitRule , metrics * RuleMetrics ) * InhibitRule {
275288 var (
276- sources []Source
289+ sources []* Source
277290 sourcem labels.Matchers
278291 targetm labels.Matchers
279292 )
280293
281294 if len (cr .Sources ) > 0 {
282295 for _ , sm := range cr .Sources {
283- sourcem = append (sourcem , sm .SrcMatchers ... )
296+ var sourcesm labels.Matchers
297+ sourcesm = append (sourcesm , sm .SrcMatchers ... )
284298 equal := map [model.LabelName ]struct {}{}
285299 for _ , ln := range sm .Equal {
286300 equal [model .LabelName (ln )] = struct {}{}
287301 }
288- sources = append (sources , Source {
289- SrcMatchers : sourcem ,
302+ sources = append (sources , & Source {
303+ SrcMatchers : sourcesm ,
290304 Equal : equal ,
305+ scache : store .NewAlerts (),
306+ sindex : newIndex (),
291307 })
292308 }
293309 } else {
@@ -359,10 +375,18 @@ func NewInhibitRule(cr config.InhibitRule, metrics *RuleMetrics) *InhibitRule {
359375// fingerprintEquals returns the fingerprint of the equal labels of the given label set.
360376func (r * InhibitRule ) fingerprintEquals (lset model.LabelSet ) model.Fingerprint {
361377 equalSet := model.LabelSet {}
362-
363378 for n := range r .Equal {
364379 equalSet [n ] = lset [n ]
365380 }
381+
382+ return equalSet .Fingerprint ()
383+ }
384+
385+ func (s * Source ) fingerprintEquals (lset model.LabelSet ) model.Fingerprint {
386+ equalSet := model.LabelSet {}
387+ for n := range s .Equal {
388+ equalSet [n ] = lset [n ]
389+ }
366390 return equalSet .Fingerprint ()
367391}
368392
@@ -400,6 +424,39 @@ func (r *InhibitRule) updateIndex(alert *types.Alert) {
400424 // If the existing alert resolves after the new alert, do nothing.
401425}
402426
427+ func (src * Source ) updateIndex (alert * types.Alert ) {
428+ fp := alert .Fingerprint ()
429+ // Calculate source labelset subset which is in equals.
430+ eq := src .fingerprintEquals (alert .Labels )
431+
432+ // Check if the equal labelset is already in the index.
433+ indexed , ok := src .sindex .Get (eq )
434+ if ! ok {
435+ // If not, add it.
436+ src .sindex .Set (eq , fp )
437+ return
438+ }
439+ // If the indexed fingerprint is the same as the new fingerprint, do nothing.
440+ if indexed == fp {
441+ return
442+ }
443+
444+ // New alert and existing index are not the same, compare them.
445+ existing , err := src .scache .Get (indexed )
446+ if err != nil {
447+ // failed to get the existing alert, overwrite the index.
448+ src .sindex .Set (eq , fp )
449+ return
450+ }
451+
452+ // If the new alert resolves after the existing alert, replace the index.
453+ if existing .ResolvedAt (alert .EndsAt ) {
454+ src .sindex .Set (eq , fp )
455+ return
456+ }
457+ // If the existing alert resolves after the new alert, do nothing.
458+ }
459+
403460// findEqualSourceAlert returns the source alert that matches the equal labels of the given label set.
404461func (r * InhibitRule ) findEqualSourceAlert (lset model.LabelSet , now time.Time ) (* types.Alert , bool ) {
405462 equalsFP := r .fingerprintEquals (lset )
@@ -420,10 +477,40 @@ func (r *InhibitRule) findEqualSourceAlert(lset model.LabelSet, now time.Time) (
420477 return nil , false
421478}
422479
480+ func (s * Source ) findEqualSourceAlert (lset model.LabelSet , now time.Time ) (* types.Alert , bool ) {
481+ equalsFP := s .fingerprintEquals (lset )
482+ sourceFP , ok := s .sindex .Get (equalsFP )
483+ if ok {
484+ alert , err := s .scache .Get (sourceFP )
485+ if err != nil {
486+ return nil , false
487+ }
488+
489+ if alert .ResolvedAt (now ) {
490+ return nil , false
491+ }
492+
493+ return alert , true
494+ }
495+
496+ return nil , false
497+ }
498+
423499func (r * InhibitRule ) gcCallback (alerts []types.Alert ) {
424500 for _ , a := range alerts {
425- fp := r .fingerprintEquals (a .Labels )
426- r .sindex .Delete (fp )
501+ if len (r .Sources ) > 0 {
502+ for _ , src := range r .Sources {
503+ if src .SrcMatchers .Matches (a .Labels ) {
504+ fp := src .fingerprintEquals (a .Labels )
505+ src .sindex .Delete (fp )
506+
507+ break
508+ }
509+ }
510+ } else {
511+ fp := r .fingerprintEquals (a .Labels )
512+ r .sindex .Delete (fp )
513+ }
427514 }
428515 if r .Name != "" {
429516 r .metrics .sourceAlertsCacheItems .With (prometheus.Labels {"rule" : r .Name }).Set (float64 (r .scache .Len ()))
@@ -446,3 +533,28 @@ func (r *InhibitRule) hasEqual(lset model.LabelSet, excludeTwoSidedMatch bool, n
446533
447534 return model .Fingerprint (0 ), false
448535}
536+
537+ func (s * Source ) hasEqual (lset model.LabelSet , excludeTwoSidedMatch bool , now time.Time , targetMatchers labels.Matchers ) (model.Fingerprint , bool ) {
538+ equal , found := s .findEqualSourceAlert (lset , now )
539+ if found {
540+ if excludeTwoSidedMatch && targetMatchers .Matches (equal .Labels ) {
541+ return model .Fingerprint (0 ), false
542+ }
543+ return equal .Fingerprint (), found
544+ }
545+
546+ return model .Fingerprint (0 ), false
547+ }
548+
549+ func (r * InhibitRule ) allSourcesSatisfied () bool {
550+ for _ , source := range r .Sources {
551+ if ! source .foundMatch {
552+ return false
553+ }
554+ }
555+ // Reset for next use.
556+ for _ , source := range r .Sources {
557+ source .foundMatch = false
558+ }
559+ return true
560+ }
0 commit comments