While working with distributed systems, you might come across transient issues where a dependent service may become unresponsive or the request may experience some sort of network issue. Because these issues may require different solutions, you might find yourself implementing retry strategies such as retrying after a delay, simply retrying, or canceling the request.

After some research I came across the NuGet package Polly, a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.

In this short article, I'll walk through some examples of implementing the library for different scenarios.

Retry Strategies & Examples

Below are some examples of how you can leverage Polly to get past the transient issue.

Retry With a Delay

There may be a time where you need to retry after a certain time. Polly allows for a retry after a certain period using the WaitAndRetry method.

In the example below, I'm imitating calling a RESTful service that may be intermittently timing out. For this scenario, we make the assumption that two retries with a delay based on the number of retries will suffice.

...

How the Policy Below Works

The example below will create a policy which will retry 2 times the first time with a 60 second delay the second time with a 120 second delay.
  1. Execute Delegate
    The retry policy will execute an HTTP Get call to the specified URL.
  2. Inner Exception Thrown
    If the HTTP Get call exceeds the specified timeout, an exception is thrown containing TaskCanceledExceptions
  3. Another Retry
    The policy will then check if another retry is allowed, if so it will wait the specified time.
  4. Wait
    Once the wait period is over, it will attempt to re-execute the action. (You'll want to account for the timeout of the client you're using to call the URL. In this example, I'm using the HttpClient and have modified the timeout to 60 seconds.)
using Polly;
using Polly.Retry;
using System;
using System.Net.Http;

namespace Retry
{
    class Program
    {
        private static readonly string URL = "http://localhost:5000/api/values";
        private static readonly RetryPolicy TimeoutRetryPolicy = Policy.HandleInner<TaskCanceledException>().WaitAndRetry(2, retryAttempt => TimeSpan.FromSeconds(60 * retryAttempt));

        static void Main(string[] args)
        {
            CallHttpService();
            Console.Read();
        }

        public static void CallHttpService()
        {
            HttpClient client = new();
            client.Timeout = TimeSpan.FromSeconds(60);            
            TimeoutRetryPolicy.Execute(() =>
            {
                HttpResponseMessage result = client.GetAsync(URL).Result;
            });
        }
    }
}

👋 PLZ SUBSCRIBE

No Spam. Unsubscribe easily at any time.

🎊 🎉 Thanks For Your Subscription!

Retry

This is your ordinary Retry. In the event that one or more retries will get you past the issue.

In the first retry, the console application attempts to read a file that does not yet exist. For the purpose of demonstration, I'm writing the file after the first retry has executed. Polly, will attempt 3 times if the exception System.IO.FileNotFoundException is triggered.

...

How the Policy Below Works

The example below will create a policy which will retry 2 times.
  1. Execute Delegate
    The policy will attempt to open a file that does not exist.
  2. Exception Is FileNotFoundException
    Because the file will not exist on the first retry, an exception will be thrown.
  3. Another Retry
    The policy will then check if another retry is allowed, if so it will execute the action again.
using Polly;
using Polly.Retry;
using System;
using System.IO;

namespace Retry
{
    class Program
    {
        private static readonly string RandomFile = @"C:\Development\tmp\" + Guid.NewGuid() + ".txt";
        private static readonly RetryPolicy FileNotFoundRetryPolicy = Policy.Handle<FileNotFoundException>().Retry(2);

        static void Main(string[] args)
        {
            ReadFile();
            Console.Read();
        }

        public static void ReadFile()
        {
            int retries = 0;
            FileNotFoundRetryPolicy.Execute(() =>
            {
                retries++;
                if (retries == 2)
                    File.WriteAllText(RandomFile, nameof(RandomFile));

                File.OpenRead(RandomFile);
            });
        }
    }
}