@@ -104,7 +104,7 @@ func GetRules() []Rule {
104
104
105
105
// ClearRules clear all the previous rules.
106
106
func ClearRules () error {
107
- _ , err := LoadRules (nil )
107
+ _ , err , _ := LoadRules (nil )
108
108
return err
109
109
}
110
110
@@ -114,10 +114,10 @@ func ClearRules() error {
114
114
//
115
115
// bool: was designed to indicate whether the internal map has been changed
116
116
// error: was designed to indicate whether occurs the error.
117
- func LoadRules ( rules []* Rule ) ( bool , error ) {
118
- // TODO in order to avoid invalid update, should check consistent with last update rules
119
- err := onRuleUpdate (rules )
120
- return true , err
117
+ // []*Rule: was designed to return failed rules. If there is an error, it returns input rules.
118
+ func LoadRules ( rules [] * Rule ) ( bool , error , [] * Rule ) {
119
+ ret , err , failedRules := onRuleUpdate (rules )
120
+ return ret , err , failedRules
121
121
}
122
122
123
123
func getBreakersOfResource (resource string ) []CircuitBreaker {
@@ -140,7 +140,7 @@ func calculateReuseIndexFor(r *Rule, oldResCbs []CircuitBreaker) (equalIdx, reus
140
140
141
141
for idx , oldTc := range oldResCbs {
142
142
oldRule := oldTc .BoundRule ()
143
- if oldRule .isEqualsTo (r ) {
143
+ if oldRule .equalsTo (r ) {
144
144
// break if there is equivalent rule
145
145
equalIdx = idx
146
146
break
@@ -169,23 +169,35 @@ func insertCbToCbMap(cb CircuitBreaker, res string, m map[string][]CircuitBreake
169
169
}
170
170
171
171
// Concurrent safe to update rules
172
- func onRuleUpdate (rules []* Rule ) (err error ) {
172
+ func onRuleUpdate (rules []* Rule ) (ret bool , err error , failedRules []* Rule ) {
173
+ var start uint64
174
+ newBreakerRules := make (map [string ][]* Rule )
175
+
173
176
defer func () {
174
177
if r := recover (); r != nil {
178
+ // Set to false since rules are not updated due to panic
175
179
var ok bool
180
+
181
+ ret = false
176
182
err , ok = r .(error )
177
183
if ! ok {
178
184
err = fmt .Errorf ("%+v" , r )
179
185
}
186
+ failedRules = rules
187
+ return
180
188
}
189
+ logging .Debug ("Time statistics(ns) for updating circuit breaker rule" , "timeCost" , util .CurrentTimeNano () - start )
190
+ logRuleUpdate (newBreakerRules )
181
191
}()
182
192
183
- newBreakerRules := make (map [string ][]* Rule )
193
+ // Preset slice capacity to avoid dynamic allocation
194
+ failedRules = make ([]* Rule , 0 , len (rules ))
184
195
for _ , rule := range rules {
185
196
if rule == nil {
186
197
continue
187
198
}
188
199
if err := IsValid (rule ); err != nil {
200
+ failedRules = append (failedRules , rule )
189
201
logging .Warn ("Ignoring invalid circuit breaking rule when loading new rules" , "rule" , rule , "err" , err )
190
202
continue
191
203
}
@@ -195,26 +207,35 @@ func onRuleUpdate(rules []*Rule) (err error) {
195
207
if ! ok {
196
208
ruleSet = make ([]* Rule , 0 , 1 )
197
209
}
210
+
211
+ // Deduplicate loading rules
212
+ for _ , cmpRule := range ruleSet {
213
+ if rule .equalsTo (cmpRule ) {
214
+ rule = nil
215
+ break
216
+ }
217
+ }
218
+ if rule == nil {
219
+ continue
220
+ }
221
+
198
222
ruleSet = append (ruleSet , rule )
199
223
newBreakerRules [classification ] = ruleSet
200
224
}
201
225
202
226
newBreakers := make (map [string ][]CircuitBreaker )
203
- // in order to avoid growing, build newBreakers in advance
227
+ // Preset slice capacity to avoid dynamic allocation
204
228
for res , rules := range newBreakerRules {
205
229
newBreakers [res ] = make ([]CircuitBreaker , 0 , len (rules ))
206
230
}
231
+ toAddBreakerRules := make (map [string ][]* Rule )
232
+ for res , rules := range toAddBreakerRules {
233
+ toAddBreakerRules [res ] = make ([]* Rule , 0 , len (rules ))
234
+ }
207
235
208
- start : = util .CurrentTimeNano ()
236
+ start = util .CurrentTimeNano ()
209
237
updateMux .Lock ()
210
- defer func () {
211
- updateMux .Unlock ()
212
- if r := recover (); r != nil {
213
- return
214
- }
215
- logging .Debug ("Time statistics(ns) for updating circuit breaker rule" , "timeCost" , util .CurrentTimeNano ()- start )
216
- logRuleUpdate (newBreakerRules )
217
- }()
238
+ defer updateMux .Unlock ()
218
239
219
240
for res , resRules := range newBreakerRules {
220
241
emptyCircuitBreakerList := make ([]CircuitBreaker , 0 , 0 )
@@ -227,6 +248,9 @@ func onRuleUpdate(rules []*Rule) (err error) {
227
248
228
249
// First check equals scenario
229
250
if equalIdx >= 0 {
251
+ newRuleSet := toAddBreakerRules [res ]
252
+ newRuleSet = append (newRuleSet , r )
253
+ toAddBreakerRules [res ] = newRuleSet
230
254
// reuse the old cb
231
255
equalOldCb := oldResCbs [equalIdx ]
232
256
insertCbToCbMap (equalOldCb , res , newBreakers )
@@ -237,6 +261,7 @@ func onRuleUpdate(rules []*Rule) (err error) {
237
261
238
262
generator := cbGenFuncMap [r .Strategy ]
239
263
if generator == nil {
264
+ failedRules = append (failedRules , r )
240
265
logging .Warn ("Ignoring the rule due to unsupported circuit breaking strategy" , "rule" , r )
241
266
continue
242
267
}
@@ -249,20 +274,41 @@ func onRuleUpdate(rules []*Rule) (err error) {
249
274
cb , e = generator (r , nil )
250
275
}
251
276
if cb == nil || e != nil {
277
+ failedRules = append (failedRules , r )
252
278
logging .Warn ("Ignoring the rule due to bad generated circuit breaker" , "rule" , r , "err" , e )
253
279
continue
254
280
}
255
281
256
- if reuseStatIdx >= 0 {
257
- breakers [res ] = append (oldResCbs [:reuseStatIdx ], oldResCbs [reuseStatIdx + 1 :]... )
282
+ newRuleSet , ok := toAddBreakerRules [res ]
283
+ if ! ok {
284
+ newRuleSet = make ([]* Rule , 0 , 1 )
258
285
}
286
+ newRuleSet = append (newRuleSet , r )
287
+ toAddBreakerRules [res ] = newRuleSet
259
288
insertCbToCbMap (cb , res , newBreakers )
289
+ // Set to true since a new rule was just added
290
+ ret = true
260
291
}
261
292
}
262
293
263
- breakerRules = newBreakerRules
294
+ // Instead of adding more new rules, the rule set could also have been reduced to less rules.
295
+ // In this case, we compare the added rules with old ones on their sizes.
296
+ if len (toAddBreakerRules ) != len (breakerRules ) {
297
+ ret = true
298
+ }
299
+ if ret == false {
300
+ for res , addRules := range toAddBreakerRules {
301
+ originRules , ok := breakerRules [res ]
302
+ if ! ok || (len (addRules ) != len (originRules )) {
303
+ ret = true
304
+ break
305
+ }
306
+ }
307
+ }
308
+
309
+ breakerRules = toAddBreakerRules
264
310
breakers = newBreakers
265
- return nil
311
+ return
266
312
}
267
313
268
314
func rulesFrom (rm map [string ][]* Rule ) []* Rule {
@@ -338,7 +384,7 @@ func IsValid(r *Rule) error {
338
384
if len (r .Resource ) == 0 {
339
385
return errors .New ("empty resource name" )
340
386
}
341
- if int (r .Strategy ) < int ( SlowRequestRatio ) || int ( r . Strategy ) > int ( ErrorCount ) {
387
+ if uint (r .Strategy ) >= uint ( UnsupportedStrategy ) {
342
388
return errors .New ("invalid Strategy" )
343
389
}
344
390
if r .StatIntervalMs <= 0 {
0 commit comments