feat: introduced ErrIgnoreSuccessfulness

This commit is contained in:
Michael Hegel 2022-12-09 17:24:26 +01:00
parent 8f946bc5b0
commit eea4aa4ab7
2 changed files with 29 additions and 28 deletions

View File

@ -20,6 +20,9 @@ var (
// RoundTripware. It wraps the two gobreaker errors (ErrTooManyRequests & ErrOpenState) so only one comparison is
// needed
ErrCircuitBreaker = errors.New("circuit breaker triggered")
// ErrIgnoreSuccessfulness
ErrIgnoreSuccessfulness = errors.New("ignored successfulness")
)
// CircuitBreakerSettings is a copy of the gobreaker.Settings, except that the IsSuccessful function is omitted since we
@ -51,7 +54,7 @@ type CircuitBreakerSettings struct {
type CircuitBreakerOptions struct {
Counter syncint64.Counter
IsSuccessful func(err error, req *http.Request, resp *http.Response) (e error, ignore bool)
IsSuccessful func(err error, req *http.Request, resp *http.Response) error
CopyReqBody bool
CopyRespBody bool
}
@ -60,8 +63,8 @@ func NewDefaultCircuitBreakerOptions() *CircuitBreakerOptions {
return &CircuitBreakerOptions{
Counter: nil,
IsSuccessful: func(err error, req *http.Request, resp *http.Response) (e error, ignore bool) {
return err, false
IsSuccessful: func(err error, req *http.Request, resp *http.Response) error {
return err
},
CopyReqBody: false,
CopyRespBody: false,
@ -91,7 +94,7 @@ func CircuitBreakerWithMetric(
}
func CircuitBreakerWithIsSuccessful(
isSuccessful func(err error, req *http.Request, resp *http.Response) (e error, ignore bool),
isSuccessful func(err error, req *http.Request, resp *http.Response) (e error),
copyReqBody bool,
copyRespBody bool,
) CircuitBreakerOption {
@ -144,15 +147,15 @@ func CircuitBreaker(set *CircuitBreakerSettings, opts ...CircuitBreakerOption) R
var resp *http.Response
// wrap the error in case it was produced because of the circuit breaker being (half-)open
if errors.Is(gobreaker.ErrTooManyRequests, err) || errors.Is(gobreaker.ErrOpenState, err) {
if errors.Is(err, gobreaker.ErrTooManyRequests) || errors.Is(err, gobreaker.ErrOpenState) {
err = keelerrors.NewWrappedError(ErrCircuitBreaker, err)
} else if err != nil {
l.Error("unexpected error in circuit breaker",
log.FError(err),
zap.String("from", fromState.String()),
zap.String("state", fromState.String()),
)
} else {
//continue with the middleware chain
// continue with the middleware chain
resp, err = next(r)
var respCopy *http.Response
@ -168,9 +171,7 @@ func CircuitBreaker(set *CircuitBreakerSettings, opts ...CircuitBreakerOption) R
}
}
var ignore bool
err, ignore = o.IsSuccessful(err, reqCopy, respCopy)
if !ignore {
if err = o.IsSuccessful(err, reqCopy, respCopy); !errors.Is(err, ErrIgnoreSuccessfulness) {
done(err == nil)
}
}

View File

@ -60,11 +60,11 @@ func TestCircuitBreaker(t *testing.T) {
keelhttp.HTTPClientWithRoundTripware(l,
roundtripware.CircuitBreaker(cbSettings,
roundtripware.CircuitBreakerWithIsSuccessful(
func(err error, req *http.Request, resp *http.Response) (error, bool) {
func(err error, req *http.Request, resp *http.Response) error {
if resp.StatusCode >= http.StatusInternalServerError {
return errors.New("invalid status code"), false
return errors.New("invalid status code")
}
return nil, false
return nil
}, false, false,
),
),
@ -148,7 +148,7 @@ func TestCircuitBreakerCopyBodies(t *testing.T) {
keelhttp.HTTPClientWithRoundTripware(l,
roundtripware.CircuitBreaker(cbSettings,
roundtripware.CircuitBreakerWithIsSuccessful(
func(err error, req *http.Request, resp *http.Response) (error, bool) {
func(err error, req *http.Request, resp *http.Response) error {
// read the bodies
_, errRead := io.ReadAll(req.Body)
require.NoError(t, errRead)
@ -159,7 +159,7 @@ func TestCircuitBreakerCopyBodies(t *testing.T) {
// also try to close one of the bodies (should also be handled by the RoundTripware)
req.Body.Close()
return err, false
return err
}, true, true,
),
),
@ -204,14 +204,14 @@ func TestCircuitBreakerReadFromNotCopiedBodies(t *testing.T) {
keelhttp.HTTPClientWithRoundTripware(l,
roundtripware.CircuitBreaker(cbSettings,
roundtripware.CircuitBreakerWithIsSuccessful(
func(err error, req *http.Request, resp *http.Response) (error, bool) {
func(err error, req *http.Request, resp *http.Response) error {
// read the bodies
_, errRead := io.ReadAll(req.Body)
if errRead != nil {
return errRead, false
return errRead
}
return err, false
return err
}, false, true,
),
),
@ -232,14 +232,14 @@ func TestCircuitBreakerReadFromNotCopiedBodies(t *testing.T) {
keelhttp.HTTPClientWithRoundTripware(l,
roundtripware.CircuitBreaker(cbSettings,
roundtripware.CircuitBreakerWithIsSuccessful(
func(err error, req *http.Request, resp *http.Response) (error, bool) {
func(err error, req *http.Request, resp *http.Response) error {
// read the bodies
_, errRead := io.ReadAll(resp.Body)
if errRead != nil {
return errRead, false
return errRead
}
return err, false
return err
}, true, false,
),
),
@ -280,11 +280,11 @@ func TestCircuitBreakerInterval(t *testing.T) {
},
},
roundtripware.CircuitBreakerWithIsSuccessful(
func(err error, req *http.Request, resp *http.Response) (error, bool) {
func(err error, req *http.Request, resp *http.Response) error {
if resp.StatusCode >= http.StatusInternalServerError {
return errors.New("invalid status code"), false
return errors.New("invalid status code")
}
return nil, false
return nil
}, false, false,
),
),
@ -343,14 +343,14 @@ func TestCircuitBreakerIgnore(t *testing.T) {
keelhttp.HTTPClientWithRoundTripware(l,
roundtripware.CircuitBreaker(cbSettings,
roundtripware.CircuitBreakerWithIsSuccessful(
func(err error, req *http.Request, resp *http.Response) (error, bool) {
func(err error, req *http.Request, resp *http.Response) error {
if req.Method == http.MethodGet {
return errors.New("some ignored error"), true
return roundtripware.ErrIgnoreSuccessfulness
}
if resp.StatusCode >= http.StatusInternalServerError {
return errors.New("invalid status code"), false
return errors.New("invalid status code")
}
return nil, false
return nil
}, false, false,
),
),