- fix: restore breaker state when circuit state is restored
- feat: allow hydration of circuit breaker
initialState
(#89) - feat: allow passing a backoff algorithm to circuit breakers'
halfOpenAfter
(#96) - feat: add new
CountBreaker
(#93) - feat: provide the attempt number to retry's context (#95)
- fix: event listener leak in
timeout
- fix: decorrelatedJitter backoff returning NaN after many iterations (#86)
- chore: remove test files from dist package (#84)
- fix: memory leak when using
timeout()
inwrap()
(#69)
- feat: add new option
abortOnReturn
to timeouts (#72)
- breaking: please see the breaking changes for the two 3.0.0-beta releases
- feat: expose
wrap()
ed policies in the merged policy (#61)
-
breaking: refactor: create policies as free-floating functions rather than Policy methods
Previously, all policies were created via something like
Policy.handleAll().retry(...)
. However, as a result, it was hard for bundlers to tree-shake Cockatiel, since thePolicy
class was used and referenced every mechanism provided in this library.Instead, policies are now created via functions that consume the base
Policy
configuration--and that configuration is started as free functions rather than static methods. For example, where you previously wrote:import { Policy } from 'cockatiel'; Policy.handleAll().retry().attempts(3);
You instead write
import { handleAll, retry } from 'cockatiel'; retry(handleAll, { attempts: 3 );
The full changes are:
Policy.retry()
->retry(policy, options)
Policy.circuitBreaker(halfOpenAfter, breaker)
->retry(policy, { halfOpenAfter: number, breaker: IBreaker })
Policy.fallback(valueOrFactory)
->fallback(policy, valueOrFactory)
Policy.wrap(...)
->wrap(...)
Policy.timeout(duration, strategy)
->timeout(duration, strategy)
Policy.bulkhead(limit[, quue])
->bulkhead(limit[, quue])
Policy.use()
->usePolicy(policy)
This resolves #50
-
breaking: refactor: remove confusing Retry builder.
Previously, this package had a builder interface on
Policy.retry()...
. However, it was confusing how the different options of the builder interacted in more complex cases. For example, both the retry policy itself and the backoff could have different max attempts.We simplified it to be a simple options object given in
policy
, where the max attempts is also given. For the backoff itself, you pass the underlying backoff generator (or a custom one)Instead of:
Policy.retry().attempts(2).delay(5)
, you can writeretry(policy, { maxAttempts: 2, backoff: new ConstantBackoff(5) })
Policy.retry().delay([100, 200, 300])
, you can writeretry(policy, { maxAttempts: 3, backoff: new IterableBackoff(100, 200, 300) })
Policy.retry().exponential(opts)
, you can writeretry(policy, { backoff: new ExponentialBackoff(opts) })
Policy.retry().delegate(fn)
, you can writeretry(policy, { backoff: new DelegateBackoff(fn) })
This is a little more verbose, but should be more clear to readers, and it also tree-shakes better.
As part of this, the
CompositeBackoff
has been removed. This was mostly an implementation detail of the retry builder internally, and can be better implemented as a custom function in aDelegateBackoff
by most consumers.This resolves #58
-
fix: TypeScript warnings when using other providers of
AbortSignal
.
-
breaking: refactor: move to using native
AbortSignal
overCancellationToken
.Previously, this package provided its own implementation of cancellation via the
CancellationTokenSource
andCancellationToken
. Now, we use the nativeAbortSignal
which is available in browsers and Node.js since Node 16. To migrate, instead of...- accessing
context.cancellationToken
, accesscontext.signal
which is anAbortSignal
, - pass in an
AbortSignal
as the second argument toPolicy.execute
, instead of aCancellationToken
, - use
signal.aborted
instead ofsignal.isCancellationRequested
to check for cancellation, - use
signal.addEventListener("abort", fn)
instead ofsignal.onCancellationRequested(fn)
to listen for cancellation, - use
new AbortController()
instead ofnew CancellationTokenSource()
, andctrl.abort()
andctrl.signal
instead ofctrl.cancel()
andctrl.token()
, - use the helper function
deriveAbortController(signal)
exported from this package instead ofnew CancellationTokenSource(parent)
.
- accessing
- feat: improve event performance
- fix: export
IDisposable
- fix: remove incorrect deprecated marker on
RetryPolicy.onGiveUp
- fix: incorrect typings in
retry().backoff()
(#34)
-
breaking: reactor: introduce a separate BackoffFactory interface for the first backoff
This only requires changes if you use retry policies in your own code, outside of the
Policy.retry()
.See #30. For some backoff policies, such as delegate and exponential policies, the first backoff was always 0, before
next()
was called. This is undesirable, and fixing it involved separating the backoff factory from the backoff itself.The backoff classes, such as
DelegateBackoff
andExponentialBackoff
, now only have anext()
method. Theduration
, which is now a property instead of a method, is only available after the firstnext()
call.For example, previously if you did this:
let backoff = new ExponentialBackoff(); while (!succeeded) { if (!tryAgain()) { await delay(backoff.duration()); backoff = backoff.next(); } }
You now need to call
next()
before you accessduration
:let backoff = new ExponentialBackoff(); while (!succeeded) { if (!tryAgain()) { backoff = backoff.next(); await delay(backoff.duration); } }
Note: if you use typescript, you will need another variable for it to understand you. Here's an example of how we use it inside the RetryPolicy.
- fix: events on the timeout policy being emitted incorrectly, or not emitted (see #27)
- feat: add an optional
CancellationToken
toIPolicy.execute
. Add cancellation awareness to all policies; see their specific documentation for more information. (see #25) - docs: fix outdated docs on
Policy.circuitBreaker
and unnecessary dashes in jsdoc comments (see #22, #23, #24)
- fix: cockatiel not working in certain browser builds
- breaking: Node versions <10 are no longer supported.
- breaking:
FallbackPolicy.onFallback
is replaced withFallbackPolicy.onFailure
. When a failure happens, a fallback will occur. - feat: add
isBrokenCircuitError
,isBulkheadRejectedError
,isIsolatedCircuitError
,isTaskCancelledError
methods to the errors and matching predicate functions. - feat: all policies now include
onFailure
andonSuccess
callbacks for monitoring purposes (see #20) - fix: add
onHalfOpen
event to the circuit breaker (see #18) - fix:
retry.exponential()
requiring an argument when it should have been optional (see #18)
- feat: add
.dangerouslyUnref
methods for timeouts and retries (#11, thanks to @novemberborn)
- fix:
Timeout.Aggressive
triggering timeouts immediately (#16, thanks to @ekillops) - fix: correctly compile to ES2018 (#10, thanks to @novemberborn)
- feat: add new
Policy.use()
decorator
- fix: wrong typing information for options to
retry.exponential()
- fix: jitter backoff not applying max delay correctly
- fix: jitter backoff adding more than intended amount of jitter
Initial Release