Combining policies
rezilience
policies can be composed into one to apply several resilience strategies as one.
A composed policy has a wider range of possible errors than an individual policy. This is made explicit by having to convert each policy to an instance of Policy
by calling .toPolicy
. Such a Policy
has a slightly different signature for the apply
method in the error type:
def apply[R, E1 <: E, A](f: ZIO[R, E1, A]): ZIO[R, PolicyError[E1], A]
A policy can be composed with another one using its compose
method, which wraps another policy around it. Below is an example of wrapping a Retry
around a RateLimiter
around a Bulkhead
. The for-comprehension is needed because policies are created as ZManaged
s.
val policy: ZIO[Scope, Nothing, Policy[Any]] = for {
rateLimiter <- RateLimiter.make(1, 2.seconds)
bulkhead <- Bulkhead.make(2)
retry <- Retry.make(Schedule.recurs(3))
} yield bulkhead.toPolicy compose rateLimiter.toPolicy compose retry.toPolicy
Composing policies requires some special care in handling policy errors, behavior-wise and type-wise. Take for example a retry around a circuit breaker.
-
Behavior: what is the desired retry behavior when a circuit breaker error is encountered? Should the call be retried or the error passed through to the caller?
-
Types: because a
Retry
is created with aSchedule
that expects a certain typeE
of errors as input, aRetry[E]
cannot be applied onZIO[R, CircuitBreakerError[E], A]
effects.
For these cases, the Retry
and CircuitBreaker
policies have a widen
method that can adapt them to a diferent type of error. For example to adapt a Retry[Throwable]
to a Retry[PolicyError[Throwable]]
:
val retry: Retry[Throwable] = ???
val retryComposable = retry.widen[PolicyError[Throwable]] { case Policy.WrappedError(e) => e }
The partial function above is made available as Policy#unwrap[E]
for convenience, so that the above can be written as
val retryComposable: Retry[PolicyError[Throwable]] = retry.widen(Policy.unwrap[Throwable])
Many variations of policy combinations are possible. The polly
project has some good advice for the order in which to compose policies: https://github.com/App-vNext/Polly/wiki/PolicyWrap#usage-recommendations.