I have a multi-layered application where Method1
calls Method2
. Inside Method2
, I have interactions with external resources like Redis and Event Hub. I have already implemented a Retry policy on Method1
.
Now, I'm considering implementing a Circuit Breaker pattern for better fault tolerance. My primary concern is where to implement this Circuit Breaker pattern. Should it be at the level of Method2
where the external resources are being accessed?
Additionally, should I implement another Retry policy specifically in Method2
, or would that be overkill since there's already a Retry policy in Main
calling Method1
?
Here's a simplified outline of the call hierarchy for context:
using Polly;
using Polly.CircuitBreaker;
using System;
class Program
{
// Define Circuit Breaker Policy as a static member
private static readonly CircuitBreakerPolicy circuitBreakerPolicy = Policy
.Handle<Exception>()
.CircuitBreaker(2, TimeSpan.FromMinutes(1));
static void Main(string[] args)
{
// Define Retry Policy
var retryPolicy = Policy
.Handle<Exception>()
.Retry(3);
// Wrap the Retry policy around Method1
try
{
retryPolicy.Execute(Method1);
}
catch (Exception e)
{
Console.WriteLine($"Failed to execute Method1. Reason: {e.Message}");
}
}
static void Method1()
{
Console.WriteLine("Executing Method1");
//Other calls to external resources
Method2();
}
static void Method2()
{
Console.WriteLine("Executing Method2");
circuitBreakerPolicy.Execute(() =>
{
// Simulate external resource call
Console.WriteLine("Calling external resources like Redis, Event Hub etc.");
// Uncomment to simulate failure
// throw new Exception("Simulated external resource failure");
});
}
}
I would love to hear some best practices or experiences on how to approach this issue.
This resiliency pattern is used to prevent overloading an already struggling service.
It detects that service is having hard time by counting either successive/consecutive failures (done by the normal Circuit Breaker) or by counting failures during a sampling period in case of fluctuating load (done by the Advanced Circuit Breaker).
If the downstream is considered unhealthy then it will not allow new requests and will short cut them with a BrokenCircuitException
.
The key point here is that you should define a CB per downstream service. If your methods are talking with multiple services then you should have multiple circuit breakers as well. Which makes sense since
So, having a single shared CB is not enough in your use case.
I'm glad to see that you have defined your circuit breaker as a shared resource. This is important since Circuit Breaker is a stateful policy (here I have detailed how it is achieved). In other words if you would re-create it for each and every external call then you would loose that information that the previous CB has already detected that the downstream is unhealthy.
So, please keep all your circuit breakers as shared and do not create them on-demand.
As I've indicated in the comments section not all actions are retryable. In order to use the retry you have to be sure that all prerequisites are met. In short:
I have detailed this topic many times so, please allow me to just add some links here: