Skip to content

Commit

Permalink
fix!: use errors.Is for context.Cancelled check
Browse files Browse the repository at this point in the history
BREAKING: also rename IgnoreContextCancellation to IgnoreContextCancelled
  • Loading branch information
costela committed Dec 29, 2023
1 parent 6b42546 commit 297d457
Show file tree
Hide file tree
Showing 5 changed files with 13 additions and 12 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ h, err := hoglet.NewCircuit(
return Foo{}, fmt.Errorf("bar is not 42")
},
hoglet.NewSlidingWindowBreaker(10, 0.1),
hoglet.WithFailureCondition(hoglet.IgnoreContextCancelation),
hoglet.WithFailureCondition(hoglet.IgnoreContextCanceled),
)
/* if err != nil ... */

Expand All @@ -38,7 +38,7 @@ these observations according to their own logic, optionally opening the circuit.

An open circuit does not allow any calls to go through, and will return an error immediately.

If the wrapped function blocks, `Circuit.Call` will block as well, but any context cancelations or expirations will
If the wrapped function blocks, `Circuit.Call` will block as well, but any context cancellations or expirations will
count towards the failure rate, allowing the circuit to respond timely to failures, while still having well-defined and
non-racy behavior around the failed function.

Expand Down
10 changes: 5 additions & 5 deletions hoglet.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func (s stateObserver[IN, OUT]) Observe(failure bool) {
// ensures the circuit opens quickly, even if the wrapped function blocks.
//
// By default, all errors are considered failures (including [context.Canceled]), but this can be customized via
// [WithFailureCondition] and [IgnoreContextCancelation].
// [WithFailureCondition] and [IgnoreContextCanceled].
//
// Panics are observed as failures, but are not recovered (i.e.: they are "repanicked" instead).
func (c *Circuit[IN, OUT]) Call(ctx context.Context, in IN) (out OUT, err error) {
Expand Down Expand Up @@ -224,7 +224,7 @@ func (c *Circuit[IN, OUT]) Call(ctx context.Context, in IN) (out OUT, err error)
obs = dedupObservableCall(obs)

obsCtx, cancel := context.WithCancelCause(ctx)
defer cancel(internalCancellation)
defer cancel(errWrappedFunctionDone)

// TODO: we could skip this if we could ensure the original context has neither cancellation nor deadline
go c.observeCtx(obs, obsCtx)
Expand All @@ -241,8 +241,8 @@ func (c *Circuit[IN, OUT]) Call(ctx context.Context, in IN) (out OUT, err error)
return c.f(ctx, in)
}

// internalCancellation is used to distinguish between internal and external (to the lib) context cancellations.
var internalCancellation = errors.New("internal cancellation")
// errWrappedFunctionDone is used to distinguish between internal and external (to the lib) context cancellations.
var errWrappedFunctionDone = errors.New("wrapped function done")

// observeCtx observes the given context for cancellation and records it as a failure.
// It assumes [Observer] is idempotent and deduplicates calls itself.
Expand All @@ -252,7 +252,7 @@ func (c *Circuit[IN, OUT]) observeCtx(obs Observer, ctx context.Context) {
<-ctx.Done()

err := ctx.Err()
if context.Cause(ctx) == internalCancellation {
if context.Cause(ctx) == errWrappedFunctionDone {
err = nil // ignore internal cancellations; the wrapped function returned already
}
obs.Observe(c.options.isFailure(err))
Expand Down
2 changes: 1 addition & 1 deletion hoglet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func TestCircuit_ignored_context_cancellation_still_returned(t *testing.T) {
return "expected", ctx.Err()
},
nil,
WithFailureCondition(IgnoreContextCancelation))
WithFailureCondition(IgnoreContextCanceled))
require.NoError(t, err)

ctx, cancel := context.WithCancel(context.Background())
Expand Down
2 changes: 1 addition & 1 deletion limiter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func Test_ConcurrencyLimiter(t *testing.T) {
wantErr: hoglet.ErrWaitingForSlot,
},
{
name: "cancelation releases with error",
name: "cancellation releases with error",
args: args{limit: 1, block: true},
calls: 1,
cancel: true,
Expand Down
7 changes: 4 additions & 3 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package hoglet

import (
"context"
"errors"
"fmt"
"time"
)
Expand Down Expand Up @@ -38,9 +39,9 @@ func WithFailureCondition(condition func(error) bool) Option {
})
}

// IgnoreContextCancelation is a helper function for [WithFailureCondition] that ignores [context.Canceled] errors.
func IgnoreContextCancelation(err error) bool {
return err != nil && err != context.Canceled
// IgnoreContextCanceled is a helper function for [WithFailureCondition] that ignores [context.Canceled] errors.
func IgnoreContextCanceled(err error) bool {
return err != nil && !errors.Is(err, context.Canceled)
}

// WithMiddleware allows wrapping the [Breaker] via a [BreakerMiddleware].
Expand Down

0 comments on commit 297d457

Please sign in to comment.