Skip to main content

Retry logic - revisited

My previous blog post gave a very short description of Polly and that we were going to use it at work. The Polly library expose a set of options how to make your software more resilient. The most common (my guess) is a simple retry functionality, retry calling e.g. the database x number of times (x can also be indefinitely). Other options are timeouts, cache functionality, fallbacks and a couple more. 

Our demand, however, didn’t exactly fit into the retry policy of Polly. We wanted to try x number of times (e.g. five times with two seconds of sleep between tries), then sleep for some time (e.g. 30 or 60 seconds) and then try again five times. Repeat until success. Also, we only wanted to log first time there is an error, and then when the try was successful (after retries, not if successful on first attempt). 


using System;
using Microsoft.Extensions.Logging;
using Polly;
using Polly.Retry;

namespace RetrySample
{
    class RetryHelper
    {
        private readonly string HAS_DONE_RETRY = "HasDoneRetry";
        private readonly string NBR_OF_RETRIES = "NbrOfRetries";

        public int Execute(Func<int> action)
        {
            int response = 0;
            RetryPolicy<int> policy = GetRetryPolicy();

            var result = policy.ExecuteAndCapture(() =>
            {
                response = action.Invoke();
                return response;
            });

            if (result.Context.ContainsKey(HAS_DONE_RETRY) && bool.Parse(result.Context[HAS_DONE_RETRY].ToString()))
            {
                Console.WriteLine($"Connection has been restored (after {result.Context[NBR_OF_RETRIES]} tries).");
            }

            return response;
        }

        private RetryPolicy<int> GetRetryPolicy()
        {
            int tmpWaitBeforeRetry = 2;
            int tmpNbrOfRetries = 5;
            int tmpWaitTime = 30;

            RetryPolicy<int> policy = Policy
                .HandleResult<int>(r => r == 0)
                .WaitAndRetryForever(
                    sleepDurationProvider: (retryCount, result, context) =>
                    {
                        if (retryCount % tmpNbrOfRetries == 0)
                        {
                            // wait a little longer
                            return TimeSpan.FromSeconds(tmpWaitTime);
                        }
                        return TimeSpan.FromSeconds(tmpWaitBeforeRetry);
                    },
                    onRetry: (result, retry, calculatedWaitDuration, context) =>
                    {
                        if (retry == 1)
                        {
							// only log first time
                            Console.WriteLine("Error sending message, retry until successful.");
                            context[HAS_DONE_RETRY] = true;
                        }
                        context[NBR_OF_RETRIES] = retry;
                    });

            return policy;
        }
    }
}
Call the method with the following:

int result = _retryHelper.Execute(() => SomeMethod());

Comments