-
Notifications
You must be signed in to change notification settings - Fork 448
Circuit breaker refactor and enhance the scalability of stat module #143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov Report
@@ Coverage Diff @@
## master #143 +/- ##
==========================================
- Coverage 43.48% 42.31% -1.17%
==========================================
Files 67 68 +1
Lines 2863 3247 +384
==========================================
+ Hits 1245 1374 +129
- Misses 1479 1737 +258
+ Partials 139 136 -3
Continue to review full report at Codecov.
|
|
Please ignore Conflicting. |
core/base/context.go
Outdated
| Output *SentinelOutput | ||
| } | ||
|
|
||
| func (ctx *EntryContext) GetError() error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just Error() or Err() is enough? See https://golang.org/doc/effective_go.html#Getters:
There's nothing wrong with providing getters and setters yourself, and it's often appropriate to do so, but it's neither idiomatic nor necessary to put Get into the getter's name. If you have a field called owner (lower case, unexported), the getter method should be called Owner (upper case, exported), not GetOwner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe Err() is better.
If EntryContext implement Error() function, will implement standard error interface implicitly.
core/base/context.go
Outdated
| rtVal, existed := ctx.Output.Attachments[EntryContextRtStatKey] | ||
| if !existed { | ||
| rt := util.CurrentTimeMillis() - ctx.StartTime() | ||
| ctx.PutRtIfAbsent(rt) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems weird to bring the operation here. Any consideration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thinking may not be right, there is no concurrency scenario. I'm going to optimize here.
core/base/context.go
Outdated
| ctx.PutRtIfAbsent(rt) | ||
| return rt | ||
| } | ||
| rt := rtVal.(uint64) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the performance OK if QPS is large?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The impact for performance is small.
I just thought about it again, maybe add a new field 'rt' in EntryContext is better.
| +----------------+ +----------------+ +----------------+ | ||
| */ | ||
| const ( | ||
| Closed Status = iota |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better to be State (corresponding to state machine).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK.
|
|
||
| // StatusSwitchListener could listen on circuit breaker status switch | ||
| type StatusSwitchListener interface { | ||
| onSwitchToClosed(prev Status, rule Rule) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make it public?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep
core/circuit_breaker/rule.go
Outdated
| // return the strategy type | ||
| BreakerStrategy() BreakerStrategy | ||
| // check whether the rule is valid and could be converted to corresponding circuit breaker | ||
| isApplicable() bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's better to return the concrete error here (and log it at caller). Example: https://github.com/alibaba/sentinel-golang/blob/master/core/system/rule_manager.go#L87
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make sense.
core/circuit_breaker/rule_manager.go
Outdated
| if !ok { | ||
| ret = make([]CircuitBreaker, 0) | ||
| ret := make([]CircuitBreaker, 0) | ||
| updateMux.Lock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RLock?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
core/circuit_breaker/rule_manager.go
Outdated
| return ret | ||
| } | ||
| ret = append(ret, resCBs...) | ||
| ret = append(ret, commonCBs...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For default rules, it does not indicate shared circuit breaker for all resources (however it might be helpful in some scenarios). Actually it means shared circuit breaker generator on resource created.
core/circuit_breaker/rule_manager.go
Outdated
| logger.Warnf("Circuit Breaker Generator for %+v is not existed.", r.BreakerStrategy()) | ||
| continue | ||
| } | ||
| cb := generator(r) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we may improve the logic: if a rule remains unchanged (identical), use the former circuit breaker instance instead of re-creating a new one.
core/circuit_breaker/rule_manager.go
Outdated
| logger.Info(sb.String()) | ||
| } | ||
|
|
||
| func ResisterStatusSwitchListeners(listeners ...StatusSwitchListener) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right.
| type StatusSwitchListener interface { | ||
| onSwitchToClosed(prev Status, rule Rule) | ||
|
|
||
| onSwitchToOpen(prev Status, rule Rule) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could also carry the triggered "snapshot" value when the circuit breaker transforms to open.
| // return true if succeed to update | ||
| func (b *circuitBreakerBase) fromHalfOpenToOpen() bool { | ||
| if b.status.casStatus(HalfOpen, Open) { | ||
| for _, listener := range b.statusSwitchListeners { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updateNextRetryTimestamp is also needed here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make sense.
6afe67f to
45687b6
Compare
|
Please also add relevant slots to the default slot chain. |
core/circuit_breaker/rule_manager.go
Outdated
| retryTimeoutMs: errRatioRule.RetryTimeout, | ||
| nextRetryTimestamp: 0, | ||
| status: status, | ||
| statusSwitchListeners: statusSwitchListeners, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new listeners will not be included if added after the circuit breaker is generated.
core/circuit_breaker/stat_slot.go
Outdated
| } | ||
|
|
||
| func (c *MetricStatSlot) OnCompleted(ctx *base.EntryContext) { | ||
| res := ctx.Resource.String() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resource.Name()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes.
| +----------------+ +----------------+ +----------------+ | ||
| */ | ||
| const ( | ||
| Closed State = iota |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better if we could add String() method for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok.
| } | ||
| } | ||
|
|
||
| // Reset init EntryContext, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reset method should be updated for new fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right
| // fromClosedToOpen update circuit breaker status machine from closed to open | ||
| // Used for opening circuit breaker from closed when checking circuit breaker | ||
| // return true if succeed to update | ||
| func (b *circuitBreakerBase) fromClosedToOpen() bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Carry the snapshot value here?
| // fromHalfOpenToOpen update circuit breaker status machine from half-open to open | ||
| // Used for failing to probe | ||
| // return true if succeed to update | ||
| func (b *circuitBreakerBase) fromHalfOpenToOpen() bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Carry the snapshot value here?
| for _, rule := range rules { | ||
| if !rule.isApplicable() { | ||
| logger.Warnf("Ignoring invalid breaker rule when loading new rules, %+v.", rule) | ||
| if err := rule.IsApplicable(); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to check nil here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make sense
core/circuit_breaker/rule_manager.go
Outdated
| if !rule.isApplicable() { | ||
| logger.Warnf("Ignoring invalid breaker rule when loading new rules, %+v.", rule) | ||
| if err := rule.IsApplicable(); err != nil { | ||
| logger.Warnf("Ignoring invalid breaker rule when loading new rules, err: %+resRules.", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's better to print both err and the bad rule?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay
f3b3d29 to
2d41d02
Compare
| } | ||
|
|
||
| func (c *MetricStatSlot) OnCompleted(ctx *base.EntryContext) { | ||
| res := ctx.Resource.Name() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to check whether this request has been blocked. If blocked, the request should not be recorded to the circuit breakers.
| } | ||
|
|
||
| func (b *circuitBreakerBase) retryTimeoutArrived() bool { | ||
| return util.CurrentTimeMillis() >= b.nextRetryTimestamp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may need to make it thread-safe when accessing and modifying nextRetryTimestamp.
| type CircuitBreaker interface { | ||
| BoundRule() Rule | ||
|
|
||
| BoundStat() interface{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make it private?
Codecov Report
@@ Coverage Diff @@
## master #143 +/- ##
==========================================
- Coverage 42.72% 41.55% -1.17%
==========================================
Files 67 68 +1
Lines 2926 3314 +388
==========================================
+ Hits 1250 1377 +127
- Misses 1537 1801 +264
+ Partials 139 136 -3
Continue to review full report at Codecov.
|
1b796b1 to
7943e1c
Compare
ff4d6c1 to
70ea8e9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
|
Awesome work. Thanks! |
Describe what this PR does / why we need it
Circuit breaker refactor and enhance the scalability of stat module
Does this pull request fix one issue?
Describe how you did it
Describe how to verify it
Special notes for reviews