Share via


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

  • Anonymous
    October 01, 2005
    The comment has been removed

  • Anonymous
    October 02, 2005
    The comment has been removed

  • Anonymous
    October 02, 2005
    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.

  • Anonymous
    October 03, 2005
    There were discussions on C# forums sometime back on including retry in the language. Don't know about the current status though

  • Anonymous
    October 07, 2005
    The comment has been removed

  • Anonymous
    November 08, 2005
    The comment has been removed

  • Anonymous
    November 08, 2005
    The comment has been removed

  • Anonymous
    November 08, 2005
    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.

  • Anonymous
    December 15, 2005
    The comment has been removed

  • Anonymous
    December 17, 2005
    The comment has been removed

  • Anonymous
    January 10, 2006
    http://davidbetz.net/winfx/2006/01/constrainted-try-with-retry-mechanism.aspx

  • Anonymous
    June 22, 2006
    Best of all people w can talk...

  • Anonymous
    June 23, 2006
    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.

  • Anonymous
    September 05, 2006
    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."

  • Anonymous
    July 29, 2007
    Thanks the ruby retry code. Exactly what I needed.

  • Anonymous
    November 21, 2007
    The comment has been removed

  • Anonymous
    April 21, 2009
    another ugly method to work around the issue using existing construct and no goto: do {  try  {    ...  }  catch ( ... )  {    ...    continue;  } } while(false);