Previously, I’ve written about using the retry pattern to perform transient fault handling in Xamarin.Forms with Polly, and using the circuit breaker pattern to handle variable length faults with Polly. The retry pattern enables an application to retry an operation in the expectation that it will eventually succeed, while the circuit breaker pattern prevents an application from performing an operation that is likely to fail.
These patterns can both increase the stability and resiliency of an application. So how do you choose which to implement in your application? Do you choose to only handle transient faults, at the expense of ignoring variable length faults? Or do you choose to ignore transient faults, and instead handle variable length faults?
Ideally you should handle both faults. An application can combine the patterns by using the retry pattern to invoke an operation through a circuit breaker. The only caveat is that the retry logic should be sensitive to any exceptions returned by the circuit breaker, and it should abandon retry attempts if the circuit breaker indicates that a fault is not transient.
This blog post will discuss using Polly’s PolicyWrap class to combine the retry and circuit breaker patterns.
Implementation
The sample application, which can be found on GitHub, is similar to the sample application from my previous blog posts in this series, with the implementation of the retry and circuit breaker patterns being provided by Polly.
Initialization
The App class in the sample application initializes the classes that are responsible for communicating with the REST service:
TodoManager = new TodoItemManager( new RestService( new ResilientRequestProvider()));
The RestService class provides data to the TodoItemManager class, with the RestService class making REST calls using the ResilientRequestProvider class, which uses Polly to combine the retry and circuit breaker patterns.
ResilientRequestProvider
The following code example shows the GetAsync method from the ResilientRequestProvider class, which makes GET requests to a specified URI:
async Task<HttpResponseMessage> HttpInvoker(Func<Task<HttpResponseMessage>> operation) { return await policyWrap.ExecuteAsync(operation); } public async Task<TResult> GetAsync<TResult>(string uri) { string serialized = null; var httpResponse = await HttpInvoker(async () => { var response = await client.GetAsync(uri); response.EnsureSuccessStatusCode(); serialized = await response.Content.ReadAsStringAsync(); return response; }); return JsonConvert.DeserializeObject<TResult>(serialized); }
The lambda expression in the GetAsync method is passed to the HttpInvoker method, which in turn passes it to the ExecuteAsync method of the PolicyWrap instance. The PolicyWrap type is a Polly type, which provides a way to combine resilience strategies. Therefore, the code in the lambda expression is what will be executed by the policy that wraps the retry and circuit breaker policies.
The following code example shows how to combine Polly’s retry and circuit breaker policies:
var retryPolicy = Policy // Don't retry if circuit breaker has broken the circuit .Handle<Exception>(e => !(e is BrokenCircuitException)) .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (exception, delay, retryCount, context) => { Debug.WriteLine($"Retry {retryCount} after {delay.Seconds} seconds delay due to {exception.Message}"); }); var circuitBreakerPolicy = Policy .Handle<Exception>() .CircuitBreakerAsync(4, TimeSpan.FromSeconds(5), // onBreak (exception, delay) => Debug.WriteLine($"Breaking the circuit for {delay.Seconds} seconds due to {exception.Message}"), // onReset () => Debug.WriteLine($"Call ok - closing the circuit again."), // onHalfOpen () => Debug.WriteLine($"Circuit is half-open. The next call is a trial.")); policyWrap = Policy.WrapAsync(retryPolicy, circuitBreakerPolicy);
The wrapped policies are executed by the PolicyWrap.ExecuteAsync method from the outermost (left) to the innermost (right), with exceptions bubbling back outwards through the layers. Therefore, retryPolicy is the outermost policy and it places its call through the circuitBreakerPolicy. The circuitBreakerPolicy is the innermost policy, and unless the circuit is open, it executes the passed delegate. The passed delegate either succeeds, returns handled results, or throws an exception. The circuit breaker will then update the circuit state for that outcome, and pass it back to the retryPolicy. The retryPolicy will then, provided that the circuit breaker hasn’t thrown a BrokenCircuitException, (for a fault) wait and retry the operation, or fail when retries are exhausted.
For more information about using Polly’s retry policy, see Transient Fault Handling in Xamarin.Forms using Polly. For more information about using Polly’s circuit breaker policy, see Fault Handling in Xamarin.Forms: Circuit Breaker using Polly.
Running the Sample Application
The sample application, which can be found on GitHub, connects to a read-only REST service hosted by Xamarin, and it’s most likely that when running the sample the GET operation will succeed on first attempt. To observe the combined retry and circuit breaker patterns in action, change the RestUrl property in the Constants class to an address that doesn’t exist – this can be accomplished by adding a random character to the end of the existing string. Then run the application and observe the output window in Visual Studio. You should see something like:
Retry 1 after 2 seconds delay due to 404 (Not Found) Retry 2 after 4 seconds delay due to 404 (Not Found) Retry 3 after 8 seconds delay due to 404 (Not Found) Breaking the circuit for 5 seconds due to 404 (Not Found)
This shows the GET operation being retried 3 times, after an exponentially increasing delay. After the third retry, the circuit is broken for the specified amount of time. Note that the values used to configure the retry and circuit breaker policies in this example are purely for demonstration purposes.
Summary
Polly is a .NET fault handling library, which includes fluent support for a number of resilience patterns. The PolicyWrap type allows policies to be easily combined, with them being executed from the outermost to the innermost.
The advantage of using Polly over implementing your own resilience patterns is that Polly includes multiple fault handling patterns that can easily be combined for additional resilience when handling faults.