@@ -3,10 +3,13 @@ package icinga2
3
3
import (
4
4
"context"
5
5
"errors"
6
+ "fmt"
6
7
"github.com/google/uuid"
8
+ lru "github.com/hashicorp/golang-lru/v2"
7
9
"github.com/icinga/icinga-notifications/internal/event"
8
10
"go.uber.org/zap"
9
11
"golang.org/x/sync/errgroup"
12
+ "math"
10
13
"net/http"
11
14
"net/url"
12
15
"time"
@@ -64,6 +67,14 @@ type Client struct {
64
67
eventDispatcherEventStream chan * eventMsg
65
68
// catchupPhaseRequest requests the main worker to switch to the catch-up-phase to query the API for missed events.
66
69
catchupPhaseRequest chan struct {}
70
+
71
+ // eventExtraTags is used to cache Checkable groups once they have been fetched from the Icinga 2 API so that they
72
+ // don't have to be fetched over again with each ongoing event. Host/Service groups are never supposed to change at
73
+ // runtime, so this cache is being refreshed once in a while when Icinga 2 dispatches an object created/deleted
74
+ // event and thus should not overload the Icinga 2 API in a large environment with numerous Checkables.
75
+ // The LRU cache size is defined as 2^17, and when the actual cached items reach this size, the oldest values
76
+ // will simply be overwritten by the new ones.
77
+ eventExtraTagsCache * lru.Cache [string , map [string ]string ]
67
78
}
68
79
69
80
// buildCommonEvent creates an event.Event based on Host and (optional) Service attributes to be specified later.
@@ -78,10 +89,9 @@ type Client struct {
78
89
// - ID
79
90
func (client * Client ) buildCommonEvent (ctx context.Context , host , service string ) (* event.Event , error ) {
80
91
var (
81
- eventName string
82
- eventUrl * url.URL
83
- eventTags map [string ]string
84
- eventExtraTags = make (map [string ]string )
92
+ eventName string
93
+ eventUrl * url.URL
94
+ eventTags map [string ]string
85
95
)
86
96
87
97
eventUrl , err := url .Parse (client .IcingaWebRoot )
@@ -99,14 +109,6 @@ func (client *Client) buildCommonEvent(ctx context.Context, host, service string
99
109
"host" : host ,
100
110
"service" : service ,
101
111
}
102
-
103
- serviceGroups , err := client .fetchHostServiceGroups (ctx , host , service )
104
- if err != nil {
105
- return nil , err
106
- }
107
- for _ , serviceGroup := range serviceGroups {
108
- eventExtraTags ["servicegroup/" + serviceGroup ] = ""
109
- }
110
112
} else {
111
113
eventName = host
112
114
@@ -118,12 +120,27 @@ func (client *Client) buildCommonEvent(ctx context.Context, host, service string
118
120
}
119
121
}
120
122
121
- hostGroups , err := client .fetchHostServiceGroups (ctx , host , "" )
122
- if err != nil {
123
- return nil , err
124
- }
125
- for _ , hostGroup := range hostGroups {
126
- eventExtraTags ["hostgroup/" + hostGroup ] = ""
123
+ extraTags := make (map [string ]string )
124
+ if existingExtraTags , ok := client .eventExtraTagsCache .Get (eventName ); ok {
125
+ extraTags = existingExtraTags
126
+ } else {
127
+ objectType := "Host"
128
+ if service != "" {
129
+ objectType = "Service"
130
+ }
131
+
132
+ checkableResult := & CheckableCreatedDeleted {
133
+ ObjectName : eventName ,
134
+ ObjectType : objectType ,
135
+ EventType : typeObjectCreated ,
136
+ }
137
+
138
+ if err := client .refreshExtraTagsCache (ctx , checkableResult ); err != nil {
139
+ return nil , err
140
+ }
141
+ if existingExtraTags , ok := client .eventExtraTagsCache .Get (eventName ); ok {
142
+ extraTags = existingExtraTags
143
+ }
127
144
}
128
145
129
146
return & event.Event {
@@ -132,10 +149,44 @@ func (client *Client) buildCommonEvent(ctx context.Context, host, service string
132
149
Name : eventName ,
133
150
URL : eventUrl .String (),
134
151
Tags : eventTags ,
135
- ExtraTags : eventExtraTags ,
152
+ ExtraTags : extraTags ,
136
153
}, nil
137
154
}
138
155
156
+ // refreshExtraTagsCache refreshes the client event extra tags cache store based on the given response result.
157
+ func (client * Client ) refreshExtraTagsCache (ctx context.Context , result * CheckableCreatedDeleted ) error {
158
+ switch result .EventType {
159
+ case typeObjectDeleted :
160
+ // The checkable has just been deleted, so delete all existing extra tags from our cache store as well.
161
+ client .eventExtraTagsCache .Remove (result .ObjectName )
162
+ case typeObjectCreated :
163
+ extraTags := make (map [string ]string )
164
+ hostGroups , err := client .fetchHostServiceGroups (ctx , result .GetHostName (), "" )
165
+ if err != nil {
166
+ return err
167
+ }
168
+ for _ , hostGroup := range hostGroups {
169
+ extraTags ["hostgroup/" + hostGroup ] = ""
170
+ }
171
+
172
+ if result .ObjectType == "Service" {
173
+ serviceGroups , err := client .fetchHostServiceGroups (ctx , result .GetHostName (), result .GetServiceName ())
174
+ if err != nil {
175
+ return err
176
+ }
177
+ for _ , serviceGroup := range serviceGroups {
178
+ extraTags ["servicegroup/" + serviceGroup ] = ""
179
+ }
180
+ }
181
+
182
+ client .eventExtraTagsCache .Add (result .ObjectName , extraTags )
183
+ default :
184
+ return fmt .Errorf ("cannot refresh object extra tags for event-stream type %s" , result .EventType )
185
+ }
186
+
187
+ return nil
188
+ }
189
+
139
190
// buildHostServiceEvent constructs an event.Event based on a CheckResult, a Host or Service state, a Host name and an
140
191
// optional Service name if the Event should represent a Service object.
141
192
func (client * Client ) buildHostServiceEvent (ctx context.Context , result CheckResult , state int , host , service string ) (* event.Event , error ) {
@@ -441,6 +492,14 @@ func (client *Client) Process() {
441
492
client .eventDispatcherEventStream = make (chan * eventMsg )
442
493
client .catchupPhaseRequest = make (chan struct {})
443
494
495
+ cache , err := lru.New [string , map [string ]string ](int (math .Exp2 (17 )))
496
+ if err != nil {
497
+ // Is unlikely to happen, as the only error being returned is triggered by
498
+ // specifying negative numbers as the cache size.
499
+ client .Logger .Fatalw ("Failed to initialise event extra tags cache" , zap .Error (err ))
500
+ }
501
+ client .eventExtraTagsCache = cache
502
+
444
503
go client .worker ()
445
504
446
505
for client .Ctx .Err () == nil {
0 commit comments