Thursday, October 21, 2010

Retry Utility

This morning I ran into a situation where I needed to retry several method calls if they failed. I decided that the easiest and cleanest way to accomplish this would be via a generic utility class. Below is the class I wrote. In the future I may add a method that will handle targets with void returns. But, for now, this is all my application required.

public class RetryUtil
{
private static readonly log4net.ILog _log = log4net.LogManager.GetLogger(typeof(RetryUtil));

/// <summary>
/// Executes the specified function.
/// </summary>
/// <typeparam name="T">Type that the target funtion returns.</typeparam>
/// <param name="func">The target function.</param>
/// <param name="requiresRetry">Function that validates whether a retry is required based on the target function's output.</param>
/// <param name="retryCount">The maximum number of times the target function should be executed.</param>
/// <param name="sleepSeconds">The number of seconds to sleep the thread between retries.</param>
/// <returns>The result of the target function.</returns>
public T Execute<T>(Func<T> func, Func<T, bool> requiresRetry, int retryCount, int sleepSeconds)
{
var attempt = 0;
while (attempt < retryCount)
{
try
{
_log.InfoFormat("Attempting call {0} of {1}", attempt + 1, retryCount);
var t = func();

if (requiresRetry(t))
{
attempt++;
if (attempt >= retryCount)
return t;

_log.WarnFormat("Target method returned {0}... Retrying...", t);

if (sleepSeconds > 0)
{
_log.InfoFormat("Sleeping thread for {0} seconds.", sleepSeconds);
Thread.Sleep(TimeSpan.FromSeconds(sleepSeconds));
}
continue;
}

return t;
}
catch (Exception ex)
{
attempt++;
_log.Error(ex);

if (attempt >= retryCount)
throw;
}
}
return default(T);
}
}


To call this utility, simply pass in a delegate (or lambda expression) that wraps your target method, a delegate (or lambda expression) that validates the return, a retry count, and the number of seconds you want to sleep the thread between retries.

public int TargetMethod(int foo)
{
//Do work..
return -1;
}

public void CallingMethod()
{
var retryUtil = new RetryUtil();

//Example call using delegates
Func<int> target = delegate { return MyTargetMethod(123); };
Func<int, bool> validate = delegate(int foo) { return foo < 0; };
var result1 = retryUtil.Execute(target, validate, 5, 10);

//Example using lambda expressions
var result2 = retryUtil.Execute(() => MyTargetMethod(123), (i=> i < 0), 5, 10);
}

No comments:

Post a Comment