I am setting up Polly policies with some variant of:
Policy
.Handle<Exception>()
.AdvancedCircuitBreakerAsync(failureThreshold, samplingDuration, minimumThroughput, durationOfBreak )
.WrapAsync(Policy.TimeoutAsync(timeout, TimeoutStrategy.Pessimistic));
I am adding them to an IReadOnlyPolicyRegistry<string>
and services dependent on Polly use the registry to get the policy.
Now I'm trying to add all circuit breaker statuses to the health check. I am trying to do this by taking the PolicyRegistry and iterating through the policies. However, the types in the registry are IAsyncPolicy
, IAsyncPolicy<HttpResponseMessage>
, etc.
Using the debugger, I can see that the Outer
property is an AsyncCircuitBreakerPolicy
, but that Outer
property is not public so I can't use it and policy as AsyncCircuitBreakerPolicy
returns null.
Does anyone know how to 'unwrap' the IAsyncPolicy
to get at the AsyncCircuitBreakerPolicy
?
Is there an out of the box solution for grabbing all circuit breakers registered with Polly?
Do I need to keep my own internal list of circuit breakers and add to it when creating the Polly policies?
Note: I want to add a health check entry for each circuit breaker even if it is closed - just so I know things were registered correctly.
In Polly almost every policy implements a bunch of interfaces:
AsyncPolicyWrap
implements IAsyncPolicy
, IsPolicy
and IPolicyWrap
Outer
/ Inner
AsyncCircuitBreakerPolicy
implements IAsyncPolicy
, IsPolicy
and ICircuitBreakerPolicy
CircuitState
The IReadOnlyPolicyRegistry<string>
is an IEnumerable<KeyValuePair<string, IsPolicy>>
so, you can iterate through it.
With a foreach
the cb's state retrieval logic looks like this:
foreach (var (key, policy) in registry)
{
if (policy is IPolicyWrap wrap)
{
if (wrap.Outer is ICircuitBreakerPolicy cb)
{
var state = cb.CircuitState;
}
}
}
The IPolicyWrap
defines a function called GetPolicy<T>
with which we can get rid of the inner if
block
foreach (var (key, policy) in registry)
{
if (policy is IPolicyWrap wrap)
{
var cb = wrap.GetPolicy<ICircuitBreakerPolicy>();
var state = cb?.CircuitState;
}
}
We can get rid of the other if
block with some Linq
foreach (var policy in registry.Select(kp => kp.Value).Where(p => p is IPolicyWrap).Cast<IPolicyWrap>())
{
var cb = policy.GetPolicy<ICircuitBreakerPolicy>();
var state = cb?.CircuitState;
}
The GetPolicy<T>
or GetPolicies<T>
have that advantage that it does not matter whether T
is the Outer
or the Inner
or the Inner
's Outer
... it will find T
or T
s anywhere in the policy chain.