C#: try and retry


In many situation when something fails (exception is thrown) in the try block you want to retry that again. This could be a network timeout exception, or some other resource unavailability where you expect the same piece of try block code to succeed if you execute it again.


How you do it in Ruby


Let us assume there is some networkAccess() function which sometime throws a TimeoutException. In Ruby you can write code as follows.

begin                  # try in C#
networkAccess()
rescue Exception => ex #same as catch (Exception ex) in c#
retry
puts Finally quiting
end

Here the retry keyword makes the preceeding try block (starting with begin in case of Ruby) to be executed again. This code can be further enhanced to create a custom Exception class which has a public attribute which is used to inform the caller whether it can call the failing routine again. In ruby this’d be

class TimeoutException < Exception
attr :okToRetry # public attribute in C#
def initialize(value) # constructor in c#
@okToRetry = value
end
end
// simulate a network access failure which succeeds on 3rd attempt


def
networkAccess()
$val += 1
raise TimeoutException.new($val < 3) #throw new TimeoutException
end

$val = 0
begin
networkAccess()
rescue TimeoutException => ex #catch (TimeoutException ex)
retry if ex.okToRetry
puts Finally quiting
end


Here the failing callee function networkAccess raises (throws) an exception using a public attribute in the exception class to signal the caller to indicate if it can be called again.


In C#


In C# unfortunately (??) there is no support for retry. We can debate whether it’d be useful, but I feel it would make the exception handling more complete and expressive if it did. There are two workarounds. One is using goto 🙂 and other is what we notmally use, by converting the exception to a return value and calling the failed routine again based on that.

TryLabel:

try

{

downloadMgr.DownLoadFile(“file:///server/file”, “c:\\file”);

Console.WriteLine(“File successfully downloaded”);

}

catch (NetworkException ex)

{

if (ex.OkToRetry)

goto TryLabel;

}



If you compare this with the retry in Ruby the difference in the expressiveness is evident.


The other options is to create a wrapper around this call and convert the exception to some return code and call that wrapper again based on whether its ok to call the failing function

public static bool Wrapper(DownloadManager downloadMgr)

{

try

{

downloadMgr.DownLoadFile(“file:///server/file”, “c:\\file”);

return true;

}

catch (NetworkException ex)

{

Console.WriteLine(“Failed to download file: {0}”, ex.Message);

return (!ex.OkToRetry);

}

}

static void Main(string[] args)

{

while (!Wrapper(downloadMgr)) ;

}


Here the wrapper converts the failure in DownloadFile to false return code and the caller goes on calling wrapper as long as return code is false.

Comments (17)

  1. One thing that worries it about having retry is that it’s easy to get into infinite loop.

    But that is minor, as your code showed.

    What is interesting is /where/ does it restart? On the begin block or on the line that had the exception

  2. abhinaba says:

    It restarts at the begin block. So you need to ensure that re-execution of code in between the begin and the point where the exception got thrown does not have any side effect

  3. Thong Nguyen says:

    I’ve already created an Action.Retry function that supports retry count and timeouts.

    It uses closures to allow you to naturally use any code you want in the retry.

    object myObj = …

    Actions.Retry<object>(delegate

    {

    myObj.NetworkAccess();

    }, TimeSpan.FromSeconds(10));

    You can also specify which exceptions to catch and wait periods between retries….

    A bit more powerful than the "retry" keyword though I can see the "retry" being of some use.

  4. Ramsey says:

    There were discussions on C# forums sometime back on including retry in the language. Don’t know about the current status though

  5. dhchait says:

    I tend to use a pattern similar to:

    foo() {

    bool keepTrying = true;

    while ( keepTrying ) {

    try {

    string filename = showDialogBox();

    Stream stream = openstream(filename);

    writestufftostream(stream);

    stream.close();

    keepTrying = false;

    } catch (Exception ex) {

    // prompt to retry fail etc.

    keepTrying = messagebox("failed: " + ex.Message + " – keep trying?");

    }

    }

    }

    pseudo-code but you get the idea. a retry keyword would fit nicely into the above scheme, i tend to do this a lot – especially in gui apps where you show a dialog then try to use the input, and may need to go back and retry it if the input is bad….

  6. mschaef says:

    "What is interesting is /where/ does it restart? On the begin block or on the line that had the exception "

    This is one of the more interesting parts of Common Lisp. It has exception handling that allows exceptions to be handled before the stack is unwound. It’s also possible for code to establish named (and unnamed) restarts that allow exception handlers to restart the code in particular spots. If you were writing a function that threw a ‘bad argument’ exception, you might offer two restarts: one to retry the call with another argument and one to abort the call and assume a particular return value. It’s key that all of this can be defined and overridden by the developer.

    More information:

    http://www.nhplace.com/kent/Papers/Condition-Handling-2001.html

  7. David Totzke says:

    This is crazy talk and just begging for trouble.

    while (!Wrapper(downloadMgr)) ;

    So somebody kicks out your network cable and the Wrapper function starts to return really fast because whatever it is calling will wise up to the fact that the network is gone and there you are in a 100% CPU tight loop that you can’t kill.

    Been there, done that, bought the T-Shirt

  8. David you are taking things too literally. No one is ever going to loop infinitely in a tight loop trying to do something on the network 🙂

    This is just a example meaning that you put all the retyring logic in the wrapper function and call it eternally. So wrapper should have the logic of counting how many retries it made and fail when it goes above some value.

  9. RichB says:

    In the past I’ve done retries using C# anonymous delegates – ie the codeblock I want to retry is first wrapped in a closure which allows me to execute it numerous times. Having it inside a delegate means it suddenly has a common interface and so the retry code can be written just once for many different types of codeblocks.

    To put another way, I’ve used the Command Pattern, but my Do() function has been simplified to just calling a delegate.

  10. Hugh Gleaves says:

    Hi

    I do like this whole retry idea, and have posted about it on GotDotNet (though not without some heated debate!)

    http://www.gotdotnet.com/Community/MessageBoard/Thread.aspx?id=352875&Page=1#353303

    I have proposed to MS that they consider extending the syntax of ‘try’ to include an option expression:

    try (expression)

    {

    code;

    }

    catch(Exception e)

    {

    handling;

    }

    This captures (I think) the key issues and implememnts them in the langauge:

    1. A loop

    2. A termination condition

    3. Execution of the handler after all retries have failed.

    I have also thought that this may be better, but this is just an idea:

    try (retries,interval)

    {

    code;

    }

    catch (Exception e)

    {

    handler;

    }

    This is better, because it forces the coder to define a) the number or repetitions (before the handler finally runs) and b) a sleep interval to avoid tigh cpu loops (but they could put 0.

    The idea here is to provide a construct

    Both of these constructs can be expanded to a while loop etc, and would be automatically generated by the compiler, much as a C# iterator gets expanded.

  11. lesbian rape says:

    Best of all people w can talk…

  12. Michael Hübner says:

    In good old Smalltalk, the search for an appropriate exception handler leaves the stack in its current state (same as in Common List), i.e. does not unwind the call stack.

    This has the advantage, that in an exception handler (usually in the top handler) a debugger view can be opened, which allows you to inspect the life data which caused the exception, at the position in the code where the exception occured.

    This safes you a lot of time when chasing errors.

  13. David Walker says:

    http://blogs.msdn.com/oldnewthing/archive/2005/11/07/489807.aspx

    From Raymond Chen:

    "I’ve seen this go wrong many times. So much so that my personal recommendation is simply never to retry automatically. If something fails, then report the failure. If the user wants to retry, let them be the ones to make that decision.

    Here’s how it goes wrong. This is a real example, but the names have been removed because I’m not trying to ridicule anybody; I want you to learn. There was a networking feature that implemented some type of distributed networking capability. It is the nature of networks to be unreliable, so the implementors of the functionality decided to retry ten times before finally giving up. The operation they were performing was implemented by another group, and that other group also decided to retry five times before giving up. That second group called a networking function with a timeout of thirty seconds. Meanwhile, the application that used this networking capability attempted the operation fifteen times.

    Let’s do some math. At the bottom was a timeout of thirty seconds. Five retries comes out to two and a half minutes. Ten retries from the next layer brings this to twenty-five minutes. Fifteen retries from the application layer takes us to over six hours. An operation that would normally have completed (with a failure code) in thirty seconds became, through the multiplicative effect of multiple layers of retrying, a six-hour marathon. And then you get a very angry call from one of your customers demanding that you deliver them a fix yesterday because this problem is taking down their entire sales force."

  14. Thanks the ruby retry code. Exactly what I needed.

  15. I had blogged earlier about Gardens Point Ruby.NET . After I read Don Box writing about it, I decided

  16. mtsay says:

    another ugly method to work around the issue using existing construct and no goto:

    do

    {

     try

     {

       …

     }

     catch ( … )

     {

       …

       continue;

     }

    }

    while(false);