feat: optimize circuit breaker algorithm (#3897)

master
Kevin Wan 9 months ago committed by GitHub
parent 9c17499757
commit 1303e0fe6f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -53,16 +53,19 @@ type (
// DoWithFallback runs the fallback if the Breaker rejects the request. // DoWithFallback runs the fallback if the Breaker rejects the request.
// If a panic occurs in the request, the Breaker handles it as an error // If a panic occurs in the request, the Breaker handles it as an error
// and causes the same panic again. // and causes the same panic again.
DoWithFallback(req func() error, fallback func(err error) error) error DoWithFallback(req func() error, fallback Fallback) error
// DoWithFallbackAcceptable runs the given request if the Breaker accepts it. // DoWithFallbackAcceptable runs the given request if the Breaker accepts it.
// DoWithFallbackAcceptable runs the fallback if the Breaker rejects the request. // DoWithFallbackAcceptable runs the fallback if the Breaker rejects the request.
// If a panic occurs in the request, the Breaker handles it as an error // If a panic occurs in the request, the Breaker handles it as an error
// and causes the same panic again. // and causes the same panic again.
// acceptable checks if it's a successful call, even if the error is not nil. // acceptable checks if it's a successful call, even if the error is not nil.
DoWithFallbackAcceptable(req func() error, fallback func(err error) error, acceptable Acceptable) error DoWithFallbackAcceptable(req func() error, fallback Fallback, acceptable Acceptable) error
} }
// Fallback is the func to be called if the request is rejected.
Fallback func(err error) error
// Option defines the method to customize a Breaker. // Option defines the method to customize a Breaker.
Option func(breaker *circuitBreaker) Option func(breaker *circuitBreaker)
@ -86,12 +89,12 @@ type (
internalThrottle interface { internalThrottle interface {
allow() (internalPromise, error) allow() (internalPromise, error)
doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error doReq(req func() error, fallback Fallback, acceptable Acceptable) error
} }
throttle interface { throttle interface {
allow() (Promise, error) allow() (Promise, error)
doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error doReq(req func() error, fallback Fallback, acceptable Acceptable) error
} }
) )
@ -122,11 +125,11 @@ func (cb *circuitBreaker) DoWithAcceptable(req func() error, acceptable Acceptab
return cb.throttle.doReq(req, nil, acceptable) return cb.throttle.doReq(req, nil, acceptable)
} }
func (cb *circuitBreaker) DoWithFallback(req func() error, fallback func(err error) error) error { func (cb *circuitBreaker) DoWithFallback(req func() error, fallback Fallback) error {
return cb.throttle.doReq(req, fallback, defaultAcceptable) return cb.throttle.doReq(req, fallback, defaultAcceptable)
} }
func (cb *circuitBreaker) DoWithFallbackAcceptable(req func() error, fallback func(err error) error, func (cb *circuitBreaker) DoWithFallbackAcceptable(req func() error, fallback Fallback,
acceptable Acceptable) error { acceptable Acceptable) error {
return cb.throttle.doReq(req, fallback, acceptable) return cb.throttle.doReq(req, fallback, acceptable)
} }
@ -168,7 +171,7 @@ func (lt loggedThrottle) allow() (Promise, error) {
}, lt.logError(err) }, lt.logError(err)
} }
func (lt loggedThrottle) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error { func (lt loggedThrottle) doReq(req func() error, fallback Fallback, acceptable Acceptable) error {
return lt.logError(lt.internalThrottle.doReq(req, fallback, func(err error) bool { return lt.logError(lt.internalThrottle.doReq(req, fallback, func(err error) bool {
accept := acceptable(err) accept := acceptable(err)
if !accept && err != nil { if !accept && err != nil {

@ -22,14 +22,14 @@ func DoWithAcceptable(name string, req func() error, acceptable Acceptable) erro
} }
// DoWithFallback calls Breaker.DoWithFallback on the Breaker with given name. // DoWithFallback calls Breaker.DoWithFallback on the Breaker with given name.
func DoWithFallback(name string, req func() error, fallback func(err error) error) error { func DoWithFallback(name string, req func() error, fallback Fallback) error {
return do(name, func(b Breaker) error { return do(name, func(b Breaker) error {
return b.DoWithFallback(req, fallback) return b.DoWithFallback(req, fallback)
}) })
} }
// DoWithFallbackAcceptable calls Breaker.DoWithFallbackAcceptable on the Breaker with given name. // DoWithFallbackAcceptable calls Breaker.DoWithFallbackAcceptable on the Breaker with given name.
func DoWithFallbackAcceptable(name string, req func() error, fallback func(err error) error, func DoWithFallbackAcceptable(name string, req func() error, fallback Fallback,
acceptable Acceptable) error { acceptable Acceptable) error {
return do(name, func(b Breaker) error { return do(name, func(b Breaker) error {
return b.DoWithFallbackAcceptable(req, fallback, acceptable) return b.DoWithFallbackAcceptable(req, fallback, acceptable)

@ -60,8 +60,9 @@ func (b *googleBreaker) allow() (internalPromise, error) {
}, nil }, nil
} }
func (b *googleBreaker) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error { func (b *googleBreaker) doReq(req func() error, fallback Fallback, acceptable Acceptable) error {
if err := b.accept(); err != nil { if err := b.accept(); err != nil {
b.markFailure()
if fallback != nil { if fallback != nil {
return fallback(err) return fallback(err)
} }
@ -69,18 +70,19 @@ func (b *googleBreaker) doReq(req func() error, fallback func(err error) error,
return err return err
} }
var success bool
defer func() { defer func() {
if e := recover(); e != nil { // if req() panic, success is false, mark as failure
if success {
b.markSuccess()
} else {
b.markFailure() b.markFailure()
panic(e)
} }
}() }()
err := req() err := req()
if acceptable(err) { if acceptable(err) {
b.markSuccess() success = true
} else {
b.markFailure()
} }
return err return err

@ -206,7 +206,7 @@ func BenchmarkGoogleBreakerAllow(b *testing.B) {
breaker := getGoogleBreaker() breaker := getGoogleBreaker()
b.ResetTimer() b.ResetTimer()
for i := 0; i <= b.N; i++ { for i := 0; i <= b.N; i++ {
breaker.accept() _ = breaker.accept()
if i%2 == 0 { if i%2 == 0 {
breaker.markSuccess() breaker.markSuccess()
} else { } else {
@ -215,6 +215,16 @@ func BenchmarkGoogleBreakerAllow(b *testing.B) {
} }
} }
func BenchmarkGoogleBreakerDoReq(b *testing.B) {
breaker := getGoogleBreaker()
b.ResetTimer()
for i := 0; i <= b.N; i++ {
_ = breaker.doReq(func() error {
return nil
}, nil, defaultAcceptable)
}
}
func markSuccess(b *googleBreaker, count int) { func markSuccess(b *googleBreaker, count int) {
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
p, err := b.allow() p, err := b.allow()

@ -24,12 +24,11 @@ func (b nopBreaker) DoWithAcceptable(req func() error, _ Acceptable) error {
return req() return req()
} }
func (b nopBreaker) DoWithFallback(req func() error, _ func(err error) error) error { func (b nopBreaker) DoWithFallback(req func() error, _ Fallback) error {
return req() return req()
} }
func (b nopBreaker) DoWithFallbackAcceptable(req func() error, _ func(err error) error, func (b nopBreaker) DoWithFallbackAcceptable(req func() error, _ Fallback, _ Acceptable) error {
_ Acceptable) error {
return req() return req()
} }

@ -603,11 +603,11 @@ func (d *dropBreaker) DoWithAcceptable(_ func() error, _ breaker.Acceptable) err
return errDummy return errDummy
} }
func (d *dropBreaker) DoWithFallback(_ func() error, _ func(err error) error) error { func (d *dropBreaker) DoWithFallback(_ func() error, _ breaker.Fallback) error {
return nil return nil
} }
func (d *dropBreaker) DoWithFallbackAcceptable(_ func() error, _ func(err error) error, func (d *dropBreaker) DoWithFallbackAcceptable(_ func() error, _ breaker.Fallback,
_ breaker.Acceptable) error { _ breaker.Acceptable) error {
return nil return nil
} }

Loading…
Cancel
Save