Cleanest Way to Write Retry Logic

Cleanest way to write retry logic?

Blanket catch statements that simply retry the same call can be dangerous if used as a general exception handling mechanism. Having said that, here's a lambda-based retry wrapper that you can use with any method. I chose to factor the number of retries and the retry timeout out as parameters for a bit more flexibility:

public static class Retry
{
public static void Do(
Action action,
TimeSpan retryInterval,
int maxAttemptCount = 3)
{
Do<object>(() =>
{
action();
return null;
}, retryInterval, maxAttemptCount);
}

public static T Do<T>(
Func<T> action,
TimeSpan retryInterval,
int maxAttemptCount = 3)
{
var exceptions = new List<Exception>();

for (int attempted = 0; attempted < maxAttemptCount; attempted++)
{
try
{
if (attempted > 0)
{
Thread.Sleep(retryInterval);
}
return action();
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
throw new AggregateException(exceptions);
}
}

You can now use this utility method to perform retry logic:

Retry.Do(() => SomeFunctionThatCanFail(), TimeSpan.FromSeconds(1));

or:

Retry.Do(SomeFunctionThatCanFail, TimeSpan.FromSeconds(1));

or:

int result = Retry.Do(SomeFunctionWhichReturnsInt, TimeSpan.FromSeconds(1), 4);

Or you could even make an async overload.

Better way to write retry logic without goto

Here is the basic logic that I would use instead of a goto statement:

bool succeeded = false;
int tries = 2;

do
{
try
{
m_Outputfile = new StreamWriter(m_Filepath, true);
m_Outputfile.Write(body);
m_Outputfile.Flush();
succeeded = true;
}
catch(Exception)
{
tries--;
}
}
while (!succeeded && tries > 0);

I just added # of tries logic, even though the original question didn't have any.

Retry logic with multiple methods

Here is an example of a simple Retry method:

bool Retry(int numberOfRetries, Action method)
{
if (numberOfRetries > 0)
{
try
{
method();
return true;
}
catch (Exception e)
{
// Log the exception
LogException(e);

// wait half a second before re-attempting.
// should be configurable, it's hard coded just for the example.
Thread.Sleep(500);

// retry
return Retry(--numberOfRetries, method);
}
}
return false;
}

It will return true if the method succeed at least once, and log any exception until then.
If the method fails on every retry, it will return false.

(Succeed means completed without throwing an exception in this case)

How to use:

Assuming sample Action (void method) and sample Func (a method with a return type)

void action(int param) {/* whatever implementation you want */}
int function(string param) {/* whatever implementation you want */}

Execute a function:

int retries = 3;
int result = 0;
var stringParam = "asdf";
if (!Retry(retries, () => result = function(stringParam)))
{
Console.WriteLine("Failed in all {0} attempts", retries);
}
else
{
Console.WriteLine(result.ToString());
}

Execute an action:

int retries = 7;
int number = 42;
if (!Retry(retries, () => action(number)))
{
Console.WriteLine("Failed in all {0} attempts", retries);
}
else
{
Console.WriteLine("Success");
}

Execute a function with an out parameter (int function(string param, out int num)):

int retries = 3;
int result = 0;
int num = 0;
var stringParam = "asdf";
if (!Retry(retries, () => result = function(stringParam, out num)))
{
Console.WriteLine("Failed in all {0} attempts", retries);
}
else
{
Console.WriteLine("{0} - {1}", result, num);
}

Generic Retry Logic for code where Constructor Can Throw

I did not realise I could put a block of code into the Lambda expression. This is exactly what I want:

Retry.Do(() => { using (DataBaseProxy db = new DataBaseProxy(dbList.Next())) { .DoSomething(); } }, new TimeSpan(0));

Cleanest way to define an expression tree?

Well, I often write returned chained conditions like:

return a == b
&& c == d
&& !d
&& foo(e, f)
;

or …

return a == b
|| c == d
|| (!d && foo(e,f))
;

I guess the idea is clear.

However, that's pretty when returning something, not in an if statement, or as like you're doing. It wouldn't feel right to make a function just to return pretty trash if you never would use that function again - in my opinion. However, it could be more readable...

Nice and clean way to retry something several times

Got tired of copy/paste the same code over and over again, so I created a method that accepts the delegate of the task that has to be done. Here it is:

//  logger declaration (I use NLog)
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
delegate void WhatTodo();
static void TrySeveralTimes(WhatTodo Task, int Retries, int RetryDelay)
{
int retries = 0;
while (true)
{
try
{
Task();
break;
}
catch (Exception ex)
{
retries++;
Log.Info<string, int>("Problem doing it {0}, try {1}", ex.Message, retries);
if (retries > Retries)
{
Log.Info("Giving up...");
throw;
}
Thread.Sleep(RetryDelay);
}
}
}

To use it, I would simply write:

TrySeveralTimes(() =>
{
string destinationVpr = Path.Combine(outdir, "durations.vpr");
File.AppendAllText(destinationVpr, file + ", " + lengthInMiliseconds.ToString() + "\r\n");
}, 10, 100);

In this example I am appending a file that gets locked with some external process, only way to write it is to retry it several times until the process is done with it...

I would sure love to see better ways of handling this particular pattern (retrying).

EDIT: I looked at Gallio in another answer, and it is really great. Look at this example:

Retry.Repeat(10) // Retries maximum 10 times the evaluation of the condition.
.WithPolling(TimeSpan.FromSeconds(1)) // Waits approximatively for 1 second between each evaluation of the condition.
.WithTimeout(TimeSpan.FromSeconds(30)) // Sets a timeout of 30 seconds.
.DoBetween(() => { /* DoSomethingBetweenEachCall */ })
.Until(() => { return EvaluateSomeCondition(); });

It does everything. It will even watch your kids while you code :) But, I strive for simplicity, and still am using .NET 2.0. So I guess that my example will still be of some use to you.

Retry on exception in a try catch block

The shortest answer to your question is "just throw a new exception".

But you probably want to be able to test Happy Path too.

Here's what I'd do:

using System;
using System.Threading;

public class Program
{
public static void Main()
{
// what happens when no problem
MyClass.happyPath = true;
MyClass myClass = new MyClass();
Console.WriteLine(myClass.MyMethod(5));

// does the retry work?
MyClass.happyPath = false;
MyClass myClass = new MyClass();
Console.WriteLine(myClass.MyMethod(5));
}
}

public class MyClass
{

public static boolean happyPath = true;

public Test(){}

public string MyMethod(int tryCount) {
while(tryCount > 0)
{
// some logic that returns the switchType
// in this case i'm just manually setting it to 1
int switchType = 1;

try {
switch(switchType)
{
case 1:

if (!happyPath) {
throw new Exception ("Oops, it didn't work!");
}
return "it worked!!!";
case 2:
break;
}
} catch (Exception e){
if (--tryCount == 0) throw new Exception("Failed");
}
}

return null;
}

}

The throw that you add inside the break statement should be caught by your catch which allows you to check out your retry logic. Suggest you add some logging.



Related Topics



Leave a reply



Submit