RetryPolicy Using Microsoft Practices Enterprise Library

In today’s software development world, almost all applications are connected. In some or many ways they’re connected to network-based services. The Internet is one such network, which sometimes is not reliable due to slow connections, timeouts, and other connection unavailability issues. It is most important for any application to handle such situations in a graceful manner. That means, these things happen and are meant to happen, so how should an application respond to these scenarios without reducing the “Reliability” factor?

An example scenario

Let’s take an example of an application that displays some information from an RSS/Atom feed. Now once in a while, the RSS/Atom host server becomes unreachable for a short span of time (say 10-20 sec.) due to the above-mentioned reasons. We have a couple of options to deal with such situations. Try reading from the feed, and when there’s an error, show the user that the feed is not available and try again. The user tries again and the application successfully reads the feed and displays the information to the user. Or the application can retry the fetch operation a couple of times, and when it’s available display the information to the user without any user interaction. This depends on the business scenario but it increases Reliability.

 Scenario  

A high-level view of the Retry mechanism

 Retry mechanism

When to apply the Retry mechanism?

In most scenarios at least, important read operations can be marked to engage the server with Retry operation for specific errors like network timeout, connection unreachable, etc. Another example that I assume everybody has seen while downloading something from the Internet and the connection goes down, is the browser waits and retries for some time before actually aborting the download.

Implementation

To introduce this behavior I prefer TrasientFaultHandling from Microsoft Practice Enterprise library. It’s available as an individual NuGet package so you won’t need to install the whole Enterprise Library monster project. Although you can use any other utility which provides a Retry mechanism or implement your own, IMO if something is already there, well-tested, and optimized then why waste time re-inventing the wheel?

Installing Nuget package

Add the NuGet package TransientFaultHandling.Core in your project to have the required library classes and methods available for usage.

 NuGet package

Implementing Transient Fault detection policy/strategy

Transient faults/exceptions are identified as temporary errors and probably can be fixed by retrying the operation. To introduce such types of fault/exceptions we need to implement the policy/strategy by implementing the interface ITransientErrorDetectionStrategy.

/// <summary>
/// Provides the transient error detection logic that can recognize transient faults when dealing with reading online feed.
/// </summary>
internal class DownloadFeedTrasientErrorDetectionStrategy : ITransientErrorDetectionStrategy
{
    /// <summary>
    /// Determines whether the specified exception represents a transient failure that can be compensated by a retry.
    /// </summary>
    /// <param name="ex">The exception object to be verified.</param>
    /// <returns>True if the specified exception is considered as transient, otherwise false.</returns>
    public bool IsTransient(Exception ex)
    {
        return CheckIsTransientInternal(ex);
    }

    /// <summary>
    /// Allows you to override the call to change or extend the behavior by extending this class.
    /// </summary>
    /// <param name="ex">The error to check.</param>
    /// <returns></returns>
    protected virtual bool CheckIsTransientInternal(Exception ex)
    {
        return CheckIsTransient(ex);
    }

    private static bool CheckIsTransient(Exception ex)
    {
        if (ex is TimeoutException)
        {
            return true;
        }
        else if (ex is WebException)
        {
            return true;
        }
        else if (ex is UnauthorizedAccessException)
        {
            return false;
        }

        return false;
    }
}

view rawDownloadFeedTrasientErrorDetectionStrategy.cs hosted by GitHub

A straightforward usage of Retry utility.

public class FeedDao
{
    private readonly RetryPolicy retryPolicy;

    public FeedDao()
    {
        // setup retry policy that will try 3 times, with fast retry for first time and then
        // with incremental interval by 1.5 seconds.
        this.retryPolicy =
            new RetryPolicy<DownloadFeedTrasientErrorDetectionStrategy>(
                new Incremental(3, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1.5))
                {
                    FastFirstRetry = true
                });

        // Set tracing if retry happened.
        this.retryPolicy.Retrying += (s, e) =>
            Trace.TraceWarning("An error occurred in attempt number {1} to download feed: {0}", e.LastException.Message, e.CurrentRetryCount);
    }

    public void GetFeed()
    {
        this.retryPolicy.ExecuteAction(() => this.DownloadFeed(feed_source));
    }

    private string DownloadFeed(string source)
    {
        // logic to retrieve feeds
        return result;
    }
}

view rawFeedDao.cs hosted by GitHub.

Code Explanation

The RetryPolicy is a class from the TransientFaultHandling namespace.

this.retryPolicy = new RetryPolicy<DownloadFeedTrasientErrorDetectionStrategy>(
    new Incremental(3, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1.5))
{
    FastFirstRetry = true
});

The above snippet is an interesting one. Here we’ve used the Generic constructor of RetryPolicy. The class Incremental is RetryStrategy which takes similar arguments as defined in the RetryPolicy class constructor.

this.retryPolicy.ExecuteAction(() => this.DownloadFeed(feed_source));

The above line shows how the actual network-based method call is wrapped with Retry logic. It takes a simple void Action delegate which is represented as LambdaExpression.

This is the simplest usage of RetryPolicy. Really easy, isn’t it? But this is all you need to do to introduce Retry stuff in your application. Who else is using, Entity Framework, Windows Azure service, and many other important products are using this library.