Click here to Skip to main content
15,867,686 members
Articles
(untagged)

Transient Exception Handling

Rate me:
Please Sign up or sign in to vote.
4.92/5 (7 votes)
2 Feb 2015CPOL2 min read 24K   8   2
Transient Exception Handling

Introduction

I am in the process of reading this very book: Cloud Design Patterns which discusses a great many patterns for the cloud (geared primarily at Azure), and there is talk of a “Circuit Breaker” and “Retry Pattern”.

The “Circuit Breaker” pattern is a new one to me, but the “Retry Pattern” is certainly something I have seen many times before. The basic idea is this:

You want to run a bit of code, but some Exception could occur, so you have some sort of retry policy in place, say one that retries every 10 seconds, that might work, if the Exception is a transient one caused by the network say.

Now I don’t know about you but I have hand coded this sort of thing a lot and never spent too much time (apart from this attempt I did when using Rx which does actually work very well:

The thing is, this is a recurring issue, so surely there is help out there for this issue. Turns out there is.

I have seen a few libraries that help out in this area, here are a few that I have found.

Palmer

This project is hosted at GutHub: https://github.com/mitchdenny/palmer/.

And here are a few examples (taken from Github).

Retry For Some Time

Retry for 15 seconds:

Retry .On<WebException>()
      .For(TimeSpan.FromSeconds(15))
      .With(context => { // Code that might periodically fail due to connectivity issues. }); 

Retry Forever

Keep Retrying:

Retry .On<WebException>()
      .Indefinitely()
      .With(context => { // Code that might throw a web exception. }); 

Multiple Exceptions

You can also deal with multiple exceptions:

Retry .On<WebException>()
      .For(5)
      .AndOn<SqlException>()
      .For(5)  
      .With(context => { // Code that might throw a web exception, or a sql exception. }); 

For more examples, check out the Palmer github link posted above.

Polly

There is another nice library, which you can use which is also hosted at GitHub, it is called “Polly”: https://github.com/michael-wolfenden/Polly

So let's see some examples, again these are taken directly from the GitHub readme.

Step 1: Specify the Type of Exceptions You Want the Policy to Handle

C#
// Single exception type 
Policy .Handle<DivideByZeroException>() 

// Single exception type with condition 
Policy .Handle<SqlException>(ex => ex.Number == 1205) 

// Multiple exception types 
Policy .Handle<DivideByZeroException>() 
       .Or<ArgumentException>() 

// Multiple exception types with condition
 Policy .Handle<SqlException>(ex => ex.Number == 1205) 
        .Or<ArgumentException>(ex => x.ParamName == "example") 

Step 2: Specify How the Policy Should Handle Those Exceptions

Retry
C#
// Retry once 
Policy .Handle<DivideByZeroException>() .Retry() 

// Retry multiple times 
Policy .Handle<DivideByZeroException>() .Retry(3) 

// Retry multiple times, calling an action on each retry 
// with the current exception and retry count 
Policy .Handle<DivideByZeroException>() 
       .Retry(3, (exception, retryCount) => { // do something }); 

// Retry multiple times, calling an action on each retry 
// with the current exception, retry count and context 
// provided to Execute() 
Policy .Handle<DivideByZeroException>() 
       .Retry(3, (exception, retryCount, context) => { // do something }); 
Retry Forever
C#
// Retry forever 
Policy .Handle<DivideByZeroException>() 
       .RetryForever() 

// Retry forever, calling an action on each retry with the 
// current exception 
Policy .Handle<DivideByZeroException>() 
       .RetryForever(exception => { // do something }); 

// Retry forever, calling an action on each retry with the 
// current exception and context provided to Execute() 
Policy .Handle<DivideByZeroException>() 
       .RetryForever((exception, context) => { // do something }); 
Retry and Wait
C#
// Retry, waiting a specified duration between each retry 
Policy .Handle<DivideByZeroException>() 
       .WaitAndRetry(new[] 
       { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3) }); 

// Retry, waiting a specified duration between each retry, 
// calling an action on each retry with the current exception 
// and duration 
Policy .Handle<DivideByZeroException>() 
       .WaitAndRetry(new[] { 1.Seconds(), 2.Seconds(), 3.Seconds() }, 
                    (exception, timeSpan) => { // do something }); 

// Retry, waiting a specified duration between each retry, 
// calling an action on each retry with the current exception, 
// duration and context provided to Execute() 
Policy .Handle<DivideByZeroException>() 
       .WaitAndRetry(new[] { 1.Seconds(), 2.Seconds(), 3.Seconds() }, 
                     (exception, timeSpan, context) => { // do something }); 

// Retry a specified number of times, using a function to 
// calculate the duration to wait between retries based on 
// the current retry attempt (allows for exponential backoff) 
// In this case will wait for 
// 1 ^ 2 = 2 seconds then 
// 2 ^ 2 = 4 seconds then 
// 3 ^ 2 = 8 seconds then 
// 4 ^ 2 = 16 seconds then 
// 5 ^ 2 = 32 seconds 
Policy .Handle<DivideByZeroException>() 
       .WaitAndRetry(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) ); 

// Retry a specified number of times, using a function to 
// calculate the duration to wait between retries based on 
// the current retry attempt, calling an action on each retry 
// with the current exception, duration and context provided 
// to Execute() 
Policy .Handle<DivideByZeroException>() 
       .WaitAndRetry( 5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), 
                 (exception, timeSpan, context) => { // do something } ) 

Step 3: Execute the Policy

C#
// Execute an action var policy = Policy .Handle<DivideByZeroException>() .Retry(); 
policy.Execute(() => DoSomething()); 

// Execute an action passing arbitrary context data 
var policy = Policy .Handle<DivideByZeroException>() 
                    .Retry(3, (exception, retryCount, context) => 
                    { var methodThatRaisedException = context["methodName"]; 

Log(exception, methodThatRaisedException); }); 

policy.Execute( () => DoSomething(), new Dictionary<string, object>() 
{{ "methodName", "some method" }} ); 

// Execute a function returning a result 
var policy = Policy .Handle<DivideByZeroException>() .Retry(); 
var result = policy.Execute(() => DoSomething()); 

// Execute a function returning a result passing arbitrary context data 
var policy = Policy .Handle<DivideByZeroException>() 
                    .Retry(3, (exception, retryCount, context) => 
                    { object methodThatRaisedException = context["methodName"]; 

Log(exception, methodThatRaisedException) }); 

var result = policy.Execute( () => DoSomething(), 
new Dictionary<string, object>() 
{{ "methodName", "some method" }} ); 

// You can of course chain it all together 
Policy .Handle<SqlException>(ex => ex.Number == 1205)
       .Or<ArgumentException>(ex => ex.ParamName == "example")
       .Retry()
       .Execute(() => DoSomething()); 

Transient Application Block

There is also the Microsoft Patterns & Practices offering “Transient Application Block”. Now P&P pattern can be, well a bit over the top to be frank. This one is not so bad though, it's mainly geared toward working with Azure, but like a lot of stuff in Azure can be used outside of Azure.

There is a nice CodeProject article on how to use this application block outside of Azure which you can read right here:

Here is a small example of how to use this block taken from the article noted here...

C#
static void Main(string[] args) 
{ 
    try 
    { 
        // Step 1 
        var retryStrategy = new Incremental
        (RETRY_COUNT, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(2)); 

        // Step 2 
        var retryPolicy = new RetryPolicy<CustomTransientErrorDetectionStrategy>(retryStrategy); 

        // Step 3 
        retryPolicy.ExecuteAction(NavigateTo); 
    } 
    catch (Exception e) 
    { 
        Console.WriteLine(e.Message); 
    } 
} 

// This is the code that may experience transient errors 
static private void NavigateTo() 
{ 
    Console.WriteLine(DateTime.Now); 
    WebClient wc = new WebClient(); 
    wc.DownloadString("<a href="file:///c://temp.txt">c:\\temp.txt</a>"); 
} 

...where the above code makes use of this custom ITransientErrorDetectionStrategy implementation:

C#
internal class CustomTransientErrorDetectionStrategy : ITransientErrorDetectionStrategy 
{ 
    public bool IsTransient(Exception ex) 
    { 
        if (ex is WebException) 
            return true; 

        return false; 
    } 
} 

Conclusion

Anyway there you go, I know I have done nothing more than paste a few links to other peoples' work here, but there may be some people that did not know about these very useful libraries and you may think aha that is the one for me. So happy fault handling.

Until next time!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
QuestionWhat happens if the user decides to kill the application? Pin
Tomaž Štih8-Sep-16 22:30
Tomaž Štih8-Sep-16 22:30 
GeneralMy vote of 5 Pin
Assil25-Feb-16 10:06
professionalAssil25-Feb-16 10:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.