-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Non generic and generic policies
TL;DR Policy<TResult>
policies generic-typed to TResult
allow compile-time type-binding and intellisense when using certain features: when configuring policies to handle TResult
types; when using Fallback<TResult>
; and when using PolicyWrap
to combine policies already strongly-typed to executions returning TResult
.
Polly offers non-generic policies: RetryPolicy
, CircuitBreakerPolicy
(etc), each extending the base non-generic type Policy
. They can be used:
- with
.Execute(Action)
, to execute delegates returningvoid
- with a generic method overload
.Execute<TResult>(Func<..., TResult>)
, to execute delegates returning anyTResult
.
They offer maximum flexibility of what can be executed through the policy, for more straightforward use cases.
For fault-handling policies such as retry or circuit-breaker, non-generic policies can be used flexibly across return types, if you are only handling exceptions with the policy, not results.
Other policy types such as TimeoutPolicy
or BulkheadPolicy
are also usually found in the non-generic form, as they do not interpret the return values of executions in any way.
While the above generic method overloads on non-generic Policy
offer flexibility, they can't offer compile-time type-binding to anything outside that execute method.
Once features are used where compile-time type-binding makes sense, Polly's configuration syntax instead returns you generic policies of the form RetryPolicy<TResult>
, CircuitBreakerPolicy<TResult>
(etc), each extending the base type Policy<TResult>
.
The features falling into this category are:
When a .HandleResult<TResult>(...)
clause is used, the generic Policy<TResult>
enforces compile-time type binding between the .HandleResult<TResult>(...)
clause and .Execute<TResult>(...)
calls made on the policy.
If this strong-typing were not used, it would be possible to write (and compile) non-sensical code such as:
Policy
.HandleResult<foo>(Func<foo, bool>)
.Retry(2)
.Execute<bar>(Func<bar>);
This was deemed unacceptable. If executing a Func<bar>
on a foo
-handling Policy was permitted, what to do?
- If the
foo/bar
mismatch were to throw an exception, then why not enforce the type matching at compile time instead of leave it to a run-time failure? - If the
foo/bar
mismatch were to not throw an exception, it would have to be silently ignored. But this would carry the grave risk of leading users into a pit of failure. Unwittingly mismatching the.HandleResult<T>()
type and the.Execute<T>()
type would lead to silent loss of operation of the Policy. This could be particularly pernicious when refactoring - a slight wrong change and, poof, your Polly protection is (silently) gone.
Typed policies Policy<TResult>
also allow type-binding for TResult
-values passed to delegate hooks such as onRetry
, onBreak
, onFallback
etc. Without generic-typed Policy<TResult>
, these would have to be passed as object
and endure ugly casting back to TResult
within the delegate hooks.
Generic policies Policy<TResult>
also allow compile-time type-binding between different Policy<TResult>
instances combined into a PolicyWrap<TResult>
.
In a policy protecting an HttpClient
call, for example, you might create a PolicyWrap<HttpResponseMessage>
combining:
- a
RetryPolicy<HttpResponseMessage>
handling particularHttpResponseMessage.StatusCode
values - a
CircuitBreakerPolicy<HttpResponseMessage>
handling particularHttpResponseMessage.StatusCode
values - a
FallbackPolicy<HttpResponseMessage>
.
The generic policies give you the compile-time intellisense to only combine these correctly, just as when coding other generic functional monads such as Linq or Rx expressions.
The alternative - permitting policy1<Foo>.Wrap(policy2<Bar>)
- would imply the same issues around swapping compile-time failure for runtime-failure, or silently dropping type mismatches, as discussed above.
The generic type Policy<TResult>
does not (and could not sensibly) extend the non-generic type Policy
. Polly's interfaces instead group what is common to non-generic and generic policies of the same policy type:
-
IsPolicy
is a marker interface: 'this is some kind of policy'- It contains only what is common to the base classes
Policy
andPolicy<TResult>
.
- It contains only what is common to the base classes
- Each specific policy-type also has an interface:
- For example,
ICircuitBreakerPolicy
groups what is common to non-generic and generic circuit breakers. -
ICircuitBreakerPolicy<TResult>
adds what is specific to the genericTResult
form, withICircuitBreakerPolicy<TResult> : ICircuitBreakerPolicy
.
- For example,
You can combine non-generic policies in with generic-TResult
policies in a PolicyWrap<TResult>
.
Returning to the preceding PolicyWrap<HttpResponseMessage>
example, you could combine in a TimeoutPolicy
(always non-generic as it doesn't handle results). For example:
TimeoutPolicy timeout = // ...
RetryPolicy<HttpResponseMessage> retry = // ...
CircuitBreakerPolicy<HttpResponseMessage> breaker = // ...
FallbackPolicy<HttpResponseMessage> fallback = // ...
// Polly permits mixing non-generic and generic policies into a generic PolicyWrap.
PolicyWrap<HttpResponseMessage> combinedResilience =
fallback
.Wrap(breaker)
.Wrap(retry)
.Wrap(timeout);
For further information on combining policies, see the PolicyWrap wiki.
- Home
- Polly RoadMap
- Contributing
- Transient fault handling and proactive resilience engineering
- Supported targets
- Retry
- Circuit Breaker
- Advanced Circuit Breaker
- Timeout
- Bulkhead
- Cache
- Rate-Limit
- Fallback
- PolicyWrap
- NoOp
- PolicyRegistry
- Polly and HttpClientFactory
- Asynchronous action execution
- Handling InnerExceptions and AggregateExceptions
- Statefulness of policies
- Keys and Context Data
- Non generic and generic policies
- Polly and interfaces
- Some policy patterns
- Debugging with Polly in Visual Studio
- Unit-testing with Polly
- Polly concept and architecture
- Polly v6 breaking changes
- Polly v7 breaking changes
- DISCUSSION PROPOSAL- Polly eventing and metrics architecture