What is the best practice of implement a ITransientErrorDetectionStrategy and setting it as the default in RetryPolicy

Topics: Transient Fault Handling Application Block ("Topaz")
Jan 9, 2013 at 6:42 AM

Hi all

 hi all

The story is before using the TransientFaultHandling library, our old code already have some kind of retry logics, now I am removing our old code and starting using the Transient Fault Handling provided by the library. I compared the logics in the in the implementation of ITransientErrorDetectionStrategy.IsTransient from the library and our old code, and found some difference. Especially for the judgment of transient for azure storage, I found some rules that only exist in our code and we want to keep.

 

So I need to somehow add the rules we want to keep.

I found in public doc there are some guideline about adding a customized retry stragety and setting it as the default choice in the configuration. But found little words on the customization of ITransientErrorDetectionStrategy. And by looking the code, it seems there is little we could to customize this rule in configuration for the default retry policy.

 

So my question is :

What is the best practice of implement a ITransientErrorDetectionStrategy and setting it as the default in RetryPolicy.

 By the way, I know that when calling the extension methods, like SqlConnectionExtensions.OpenWithRetry(this SqlConnection connection), I could pass a customized retry policy but I just want to know if there is a better way of doing this.

 

Thanks,

 

-steven

 

 

 

 

Jan 14, 2013 at 5:55 AM
Edited Jan 14, 2013 at 6:02 AM

That's an interesting question.  Just for reference the following links touch on this topic (probably what you were reading):

The last link shows how to implement the ITransientErrorDetectionStrategy but not how to integrate with the block.  There does not appear to be a configuration based approach that will seamlessly allow the definition of one or more custom ITransientErrorDetectionStrategy's that can be set as the default.  Perhaps someone from the P&P team might weight in on that?

The easiest way to get the custom RetryPolicy is to just ask for it:

var retryManager = EnterpriseLibraryContainer.Current.GetInstance<RetryManager>();
var retryPolicy = retryManager.GetRetryPolicy<SqlDeadlockDetectionStrategy>(retryManager.DefaultSqlConnectionStrategyName);

// Do some work that may result in a transient fault.
retryPolicy.ExecuteAction(
    () =>
    {
        DoDatabaseAction();
    });

In the above code, SqlDeadlockDetectionStrategy is my custom ITransientErrorDetectionStrategy.  The downside of this is that we need to ask for the specific ITransientErrorDetectionStrategy each time so we are not using a "default" detection strategy.    

You could always create your own facade helper class that returns what you think the default is.  Another, elegant, way to do something very similar (not sure if it is "best") is to create a custom RetryManager where we will set our custom ITransientErrorDetectionStrategy:

public class CustomRetryManager : RetryManagerImpl
{
    public CustomRetryManager(IEnumerable<RetryStrategy> retryStrategies, RetryManager rm, ITransientErrorDetectionStrategy defaultSqlConnectionDetectionStrategy)
        : base(retryStrategies, rm.DefaultRetryStrategyName, rm.DefaultSqlConnectionStrategyName, rm.DefaultSqlCommandStrategyName, rm.DefaultAzureServiceBusStrategyName, rm.DefaultAzureCachingStrategyName, rm.DefaultAzureStorageStrategyName)
    {
        this.DefaultSqlConnectionDetectionStrategy = defaultSqlConnectionDetectionStrategy;
    }

    public ITransientErrorDetectionStrategy DefaultSqlConnectionDetectionStrategy
    {
        get;
        private set;
    }

    public override RetryPolicy GetDefaultSqlConnectionRetryPolicy()
    {
        return new RetryPolicy(DefaultSqlConnectionDetectionStrategy, this.GetDefaultSqlConnectionRetryStrategy());
    }
}

I'm extending RetryManagerImpl which may not be the best idea since that is the internal implementation class.  There are only 2 constructors and an override on that class so you could choose to move that into the CustomRetryManager (code duplication vs. isolation from changes).  You could also add properties for all ITransientErrorDetectionStrategy's (the next logical step would be extend that to the configuration but that would involve changing the block).

Now when the application starts you would register the CustomRetryManager as the RetryManager for Enterprise Library:

var retryManager = EnterpriseLibraryContainer.Current.GetInstance<RetryManager>();
var customRetryManager = new CustomRetryManager(
    EnterpriseLibraryContainer.Current.GetAllInstances<RetryStrategy>(), 
    retryManager, 
    new SqlDeadlockDetectionStrategy());

IUnityContainer container = new UnityContainer();
container.AddNewExtension<EnterpriseLibraryCoreExtension>();

container.RegisterInstance<RetryManager>(customRetryManager);

EnterpriseLibraryContainer.Current = new UnityServiceLocator(container); 

Then you could use the block as usual either using Unity for dependency injection or the Enterprise Library Container as a service locator (not usually the preferred approach but good for showing an example):

var retryManager = EnterpriseLibraryContainer.Current.GetInstance<RetryManager>();

var retryPolicy = retryManager.GetDefaultSqlConnectionRetryPolicy();

retryPolicy.ExecuteAction(
    () =>
    {
        DoDatabaseAction();
    });

Another approach would be to modify the source code for the Transient Fault Application Block to include the relevant functionality.

--
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to