How can I improve this exception retry scenario? - c#

I have a web service method I am calling which is 3rd party and outside of my domain. For some reason every now and again the web service fails with a gateway timeout. Its intermittent and a call to it directly after a failed attempt can succeed.
Now I am left with a coding dilemma, I have code that should do the trick, but the code looks like amateur hour, as you'll see below.
Is this really bad code, or acceptable given the usage? If its not acceptable, how can I improve it?
Please try hard to keep a straight face while looking at it.
try
{
MDO = OperationsWebService.MessageDownload(MI);
}
catch
{
try
{
MDO = OperationsWebService.MessageDownload(MI);
}
catch
{
try
{
MDO = OperationsWebService.MessageDownload(MI);
}
catch
{
try
{
MDO = OperationsWebService.MessageDownload(MI);
}
catch
{
try
{
MDO = OperationsWebService.MessageDownload(MI);
}
catch (Exception ex)
{
// 5 retries, ok now log and deal with the error.
}
}
}
}
}

You can do it in a loop.
Exception firstEx = null;
for(int i=0; i<5; i++)
{
try
{
MDO = OperationsWebService.MessageDownload(MI);
firstEx = null;
break;
}
catch(Exception ex)
{
if (firstEx == null)
{
firstEx = ex;
}
Thread.Sleep(100 * (i + 1));
}
}
if (firstEx != null)
{
throw new Exception("WebService call failed after 5 retries.", firstEx);
}

Here's another way you might try:
// Easier to change if you decide that 5 retries isn't right for you
Exception exceptionKeeper = null;
for (int i = 0; i < MAX_RETRIES; ++i)
{
try
{
MDO = OperationsWebService.MessageDownload(MI);
break; // correct point from Joe - thanks.
}
catch (Exception ex)
{
exceptionKeeper = ex;
// 5 retries, ok now log and deal with the error.
}
}
I think it documents the intent better. It's less code as well; easier to maintain.

All of the answers so far assume that the reaction to any exception should be to retry the operation. This is a good assumption right up until it's a false assumption. You could easily be retrying an operation that is damaging your system, all because you didn't check the exception type.
You should almost never use a bare "catch", nor "catch (Exception ex). Catch a more-specific exception - one you know you can safely recover from.

Try a loop, with some kind of limit:
int retryCount = 5;
var done = false;
Exception error = null;
while (!done && retryCount > 0)
{
try
{
MDO = OperationsWebService.MessageDownload(MI);
done = true;
}
catch (Exception ex)
{
error = ex;
}
if (done)
break;
retryCount--;
}

You should use recursion (or a loop), and should only retry if you got the error you expected.
For example:
static void TryExecute<TException>(Action method, Func<TException, bool> retryFilter, int maxRetries) where TException : Exception {
try {
method();
} catch(TException ex) {
if (maxRetries > 0 && retryFilter(ex))
TryExecute(method, retryFilter, maxRetries - 1);
else
throw;
}
}
EDIT: With a loop:
static void TryExecute<TException>(Action method, Func<TException, bool> retryFilter, int maxRetries) where TException : Exception {
while (true) {
try {
method();
return;
} catch(TException ex) {
if (maxRetries > 0 && retryFilter(ex))
maxRetries--;
else
throw;
}
}
}
You can try to prevent future errors in retryFilter, perhaps by Thread.Sleep.
If the last retry fails, this will throw the last exception.

Here is some retry logic we are using. We don't do this a lot and I was going to pull it out and document it as our Retry Pattern/Standard. I had to wing it when I first wrote it so I came here to see if I was doing it correctly. Looks like I was. The version below is fully commented. See below that for an uncommented version.
#region Retry logic for SomeWebService.MyMethod
// The following code wraps SomeWebService.MyMethod in retry logic
// in an attempt to account for network failures, timeouts, etc.
// Declare the return object for SomeWebService.MyMethod outside of
// the following for{} and try{} code so that we have it afterwards.
MyMethodResult result = null;
// This logic will attempt to retry the call to SomeWebService.MyMethod
for (int retryAttempt = 1; retryAttempt <= Config.MaxRetryAttempts; retryAttempt++)
{
try
{
result = SomeWebService.MyMethod(myId);
// If we didn't get an exception, then that (most likely) means that the
// call was successful so we can break out of the retry logic.
break;
}
catch (Exception ex)
{
// Ideally we want to only catch and act on specific
// exceptions related to the failure. However, in our
// testing, we found that the exception could be any type
// (service unavailable, timeout, database failure, etc.)
// and attempting to trap every exception that was retryable
// was burdensome. It was easier to just retry everything
// regardless of the cause of the exception. YMMV. Do what is
// appropriate for your scenario.
// Need to check to see if there will be another retry attempt allowed.
if (retryAttempt < Config.MaxRetryAttempts)
{
// Log that we are re-trying
Logger.LogEvent(string.Format("Retry attempt #{0} for SomeWebService.MyMethod({1})", retryAttempt, myId);
// Put the thread to sleep. Rather than using a straight time value for each
// iteration, we are going to multiply the sleep time by how many times we
// have currently tried to call the method. This will allow for an easy way to
// cover a broader range of time without having to use higher retry counts or timeouts.
// For example, if MaxRetryAttempts = 10 and RetrySleepSeconds = 60, the coverage will
// be as follows:
// - Retry #1 - Sleep for 1 minute
// - Retry #2 - Sleep for 2 minutes (covering three minutes total)
// - Retry #10 - Sleep for 10 minutes (and will have covered almost an hour of downtime)
Thread.Sleep(retryAttempt * Config.RetrySleepSeconds * 1000);
}
else
{
// If we made it here, we have tried to call the method several
// times without any luck. Time to give up and move on.
// Moving on could either mean:
// A) Logging the exception and moving on to the next item.
Logger.LogError(string.Format("Max Retry Attempts Exceeded for SomeWebService.MyMethod({0})", MyId), ex);
// B) Throwing the exception for the program to deal with.
throw new Exception(string.Format("Max Retry Attempts Exceeded for SomeWebService.MyMethod({0})", myId), ex);
// Or both. Your code, your call.
}
}
}
#endregion
I like Samuel Neff's example of using an exception variable to see if it completely failed or not. That would have made some of the evaluations in my logic a little simpler. I could go either way. Not sure that either way has a significant advantage over the other. However, at this point in time, I'm not going to change how we do it. The important thing is to document what you are doing and why so that some idiot doesn't come through behind you and muck with everything.
Just for kicks though, to get a better idea if the code is any shorter or cleaner one way or the other, I pulled out all the comments. They came out exactly the same number of lines. I went ahead and compiled the two versions and ran them through Reflector Code Metrics and got the following:
Metric: Inside-Catch / Outside-For
CodeSize: 197 / 185
CyclomaticComplexity: 3 / 3
Instructions: 79 / 80
Locals: 6 / 7
Final exception logic inside the catch (22 lines):
MyMethodResult result = null;
for (int retryAttempt = 1; retryAttempt <= Config.MaxRetryAttempts; retryAttempt++)
{
try
{
result = SomeWebService.MyMethod(myId);
break;
}
catch (Exception ex)
{
if (retryAttempt < Config.MaxRetryAttempts)
{
Logger.LogEvent(string.Format("Retry attempt #{0} for SomeWebService.MyMethod({1})", retryAttempt, myId);
Thread.Sleep(retryAttempt * Config.RetrySleepSeconds * 1000);
}
else
{
Logger.LogError(string.Format("Max Retry Attempts Exceeded for SomeWebService.MyMethod({0})", MyId), ex);
throw new Exception(string.Format("Max Retry Attempts Exceeded for SomeWebService.MyMethod({0})", myId), ex);
}
}
}
Final exception logic after the for-loop (22 lines):
MyMethodResult result = null;
Exception retryException = null;
for (int retryAttempt = 1; retryAttempt <= Config.MaxRetryAttempts; retryAttempt++)
{
try
{
result = SomeWebService.MyMethod(myId);
retryException = null;
break;
}
catch (Exception ex)
{
retryException = ex;
Logger.LogEvent(string.Format("Retry attempt #{0} for SomeWebService.MyMethod({1})", retryAttempt, myId);
Thread.Sleep(retryAttempt * Config.RetrySleepSeconds * 1000);
}
}
if (retryException != null)
{
Logger.LogError(string.Format("Max Retry Attempts Exceeded for SomeWebService.MyMethod({0})", MyId), ex);
throw new Exception(string.Format("Max Retry Attempts Exceeded for SomeWebService.MyMethod({0})", myId), ex);
}

I'm using the following generic method for a retry scenario. I especially want to draw attention to the PreserveStackTrace method which helps to preserve the full call stack trace, because (as I learned the hard way) neither throw or throw ex yields the complete call stack trace information.
public static void RetryBeforeThrow<T>(Action action, int retries, int timeout) where T : Exception
{
int tries = 1;
do
{
try
{
action();
return;
}
catch (T ex)
{
if (retries <= 0)
{
PreserveStackTrace(ex);
throw;
}
Thread.Sleep(timeout);
}
}
while (tries++ < retries);
}
/// <summary>
/// Sets a flag on an <see cref="T:System.Exception"/> so that all the stack trace information is preserved
/// when the exception is re-thrown.
/// </summary>
/// <remarks>This is useful because "throw" removes information, such as the original stack frame.</remarks>
/// <see href="http://weblogs.asp.net/fmarguerie/archive/2008/01/02/rethrowing-exceptions-and-preserving-the-full-call-stack-trace.aspx"/>
public static void PreserveStackTrace(Exception ex)
{
MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic);
preserveStackTrace.Invoke(ex, null);
}

As everyone else has pointed out the correct approach is to wrap your try/catch inside some loop with a MAX_RETRY of some sort.
You might also consider adding a timeout between each loop iteration. Otherwise you're likely to burn through your retry counter before the transient issue has had a chance to resolve itself.

It seems you have the answers you need, but I thought I'd post this link, What is an Action Policy?, that I found to provide a much more elegant solution. Lokad has some rather labyrinthine implementations, but the guy's logic is pretty solid, and the end code you'd end up writing is pretty and simple.

int cnt=0;
bool cont = true;
while (cont)
{
try
{
MDO = OperationsWebService.MessageDownload(MI);
cont = false;
}
catch (Exception ex)
{
++cnt;
if (cnt == 5)
{
// 5 retries, ok now log and deal with the error.
cont = false;
}
}
}
UPDATED : Fixed code based on comments.

Related

C# Compare Exceptions

I have a piece of code that sometimes throws an exception and sometimes doesn't. Depending on IO and network, different kinds of exception might be thrown. I have a wrapper for that code that shall retry the failing statement, say 10 times until it succeeds. If the exception does not go away after the 10th time, I rethrow it. Until now, I know what to do. Currently, I have something like this:
private void ExecuteStatement(Action statementToExecute)
{
while (true)
{
int exceptionCount = 0;
try
{
statementToExecute.Invoke();
// statement finished successfully
break;
}
catch (Exception e)
{
if (!IsFrameworkException(e))
throw; // rethrow as it isn't a framework exception
// we know now that it's a framework exception
exceptionCount++;
if (exceptionCount >= MaxRetriesOnFrameworkException)
throw;
}
}
}
The issue is the following: I want to reset the counter in case the exception changes (i. e. different type, different stacktrace, different message). I know how to compare them individually, but I want to know if there's a standard C# way of accomplishing this.
Edit: Here's the code of IsFrameworkException:
private bool IsFrameworkException(Exception e)
{
Exception rootCause = e.GetBaseException();
return rootCause.GetType() == typeof(WebException);
}
This is not specifically related to exception handling, but a simple way to keep counters by a specific key is to use a Tuple as the key (it implements equality by value out of the box).
Note that this doesn't reset the counter on key change, it justs keeps separate counters for each key:
readonly Dictionary<Tuple<Type, string, string>, int> _counters =
new Dictionary<Tuple<Type, string, string>, int>();
bool IsCountExceeded(Exception ex)
{
// create your key with whatever properties you consider to
// be "unique"
var key = Tuple.Create(ex.GetType(), ex.StackTrace, ex.Message);
// increase counter
int counter;
if (!_counters.TryGetValue(key, out counter))
{
counter = 1;
}
else
{
counter++;
}
_counters[key] = counter;
return counter > Max;
}
You can of course always make it more readable by creating your own class, which will implement Equals/GetHashCode properly.
I am also not sure that the stack trace can be considered as a unique property, and of course, catching and ignoring exceptions is usually a bad idea.
You should specify the exception type you want to catch. Place the catch blocks in order of most specific type to most generic type.
It is not clear if the exceptions all derive from FrameworkException or if you want to use Exception, the following assumes the former.
int exceptionCount = 0;
Type lastCaughtException = null;
while(true) {
try
{
statementToExecute.Invoke();
// statement finished successfully
break;
}
catch (WebException ex)
{
if(lastCaughtException == null || lastCaughtException != ex.GetType())
exceptionCount = 0;
else
exceptionCount++;
lastCaughtException = ex.GetType();
if (exceptionCount >= MaxRetriesOnFrameworkException)
throw;
}
catch (Exception) // if you do not want to log this you can completely omit this catch
{
throw; // rethrow as it isn't a framework exception
}
}
See also try-catch (C# Reference)

How to handle exception in catch block?

I am trying to get the ideal way to handle exception. I googled & read that I should put try catch in the catch block as well to handle but what if any exception occurs in the nested block itself.
try
{
int a = 10;
int b = 0;
int c = a / b;
Console.WriteLine(c);
Console.ReadKey();
}
catch (Exception ex)
{
int a = 10; int b = 0;
int c = a / b;
Console.WriteLine(ex.Message.ToString());
Console.ReadKey();
}
finally
{
Console.WriteLine("Some Exception");
}
On googling I read that it should be decorated as below:
If exception occurs in Catch block itself then how to handle it in C#?
If exception occurs in Catch block itself then how to handle it in C#?
What happens if an exception occurs in Catch block in C#. Also what would be the caller result in that case
try
{
int a = 10;
int b = 0;
int c = a / b;
Console.WriteLine(c);
Console.ReadKey();
}
catch (Exception ex)
{
try
{
}
catch(Exception innerEx)
{
// What if exception here also occurs.
}
}
finally
{
Console.WriteLine("Some Exception");
}
If I do this way, then it will stuck in an infinite try-catch block.
I think there would be some better or the right way to handle this scenario.
I think there would be some better or the right way to handle this scenario.
No snark intended in this but simply, don't allow an exception to happen in the first place.
A try...catch is a language construct that ensures you handle an edge case or error you didn't mitigate and design for in the first place, hence why it's exceptional code.
In your code, you're simply throwing an error because of a division by 0, but in the real-world, you want to handle that and alert the user (or developer, or server, or whatever), and then handle the actual exceptional code, example:
static void PrintError()
{
Console.WriteLine("You must enter a valid number between {0} and {1}, excluding 0", int.MaxValue, int.MinValue);
}
static void Main(string[] args)
{
try {
int a = 10;
int b = 0;
PrintError(); // Tell user to enter valid numbers
while (b == 0) {
string user_input = Console.ReadLine();
if (int.TryParse(user_input, out b)) { // is it an actual number?
if (b == 0) { // no 0's remember user!??
PrintError();
} else {
// ok, everything checks out, so do what the system can actually handle without throwing an error
Console.WriteLine("a/b = {0}", (a / b));
}
} else {
PrintError();
}
}
} catch (Exception ex) {
Console.WriteLine("Something exceptional happened: {0}", ex);
}
}
This example could be simplified further, but it demonstrates there isn't an exception that could actually occur except something that is actually exceptional (i.e. out of memory error or some other system error).
In the event of larger code bases with multiple classes, the exception handler and finalizer would be where you could clean up resources acquired in other areas of the code, like closing a socket or file handle to ensure data is not lost.
In the event an error happens in the exception handler (something that can and does happen), you need to be aware of that and know what might happen in that case.
In the event of a C# application utilizing the .NET framework, an exception thrown within an exception will just cause the application to crash with the inner exception stack trace (versus the "outer" exception that's probably more relevant to the actual exception) if not handled.
There's plenty of "wrong" ways to handle exceptions (like not handling them at all), but there's not really a "right" way given the variable nature of exceptions.
Hope that can help.
First of all you need to know what does try,catch and finally works lets start:
Try: In this block we can write code which have the possibilities to throw some error (Better practice is to write code part in it.)
Catch: It is responsible to show error and what to do if error arises(Like in your code 10/0 throws error which can be handled in this section.)
Finally: Code written in this part will execute any how weather any error comes in or not.
Now for your query it would be better that you can use If...else in finally and code put in that part would be kept in try catch block.
For example:
bool flagCatch=false;
try
{
int a = 10;
int b = 0;
int c = a / b;
Console.WriteLine(c);
Console.ReadKey();
}
catch (Exception ex)
{
//Error handling
flagCatch=true;
Console.WriteLine(ex.Message.ToString());
Console.ReadKey();
}
finally
{
try
{
if(flagCatch)
{
//Code
}
else
{
//Code when error not comes
}
}
catch(Exception err)
{
//Error handling
}
}
I would go with the comment of Tieson T. . From my point of view it is an design issue.
I could also build an example with if statements -> if that goes wrong, I perform failure handling -> if the failure handling goes wrong, I perform failure handling, If the failure handling goes wrong ....
To make the code more readable, you can try to "hide" the try-catch blocks in method like:
static void PerformInTryCatch<T, TException>(Action<T> action, T obj) where TException : Exception
{
try
{
action(obj);
}
catch (TException exception)
{
// Perform some logging
}
}
Hope that helps.

Why exception filters are preferable to catching and rethrowing?

Based on this question (What benefit does the new Exception filter feature provide?).
The statement:
Exception filters are preferable to catching and rethrowing because
they leave the stack unharmed. If the exception later causes the stack
to be dumped, you can see where it originally came from, rather than
just the last place it was rethrown.
after doing some testing, I did not see the difference between both, the old and the new, I still see the exception from the place it was rethrown. So, or the information is not confirmed, I don't understand the Exception filters( that is why I am asking), or I am doing it wrong. Can you explaing me why this action filter are an advantage?
class specialException : Exception
{
public DateTime sentDateTime { get; } = DateTime.Now;
public int code { get; } = 0;
public string emailsToAlert { get; } = "email#domain.com";
}
then:
try
{
throw new specialException(); //line 16
throw new Exception("Weird exception");
//int a = Int32.Parse("fail");
}
catch (specialException e) when(e.code == 0)
{
WriteLine("E.code 0");
throw; // <-Line 23
}
catch (FormatException e)
{
WriteLine("cond1 " + e.GetBaseException().Message+" "+e.StackTrace);
throw;
}
catch (Exception e) //when (cond2)
{
Console.WriteLine("cond2! " + e.Message);
throw;
}
Result:
The advantages of exception filtering are more to do with when the filter doesn't match, not when it does match. If you remove all of the catch blocks except for the first one, and change the filter on the first catch block to when(e.code != 0), then the callstack of the exception would indicate it was thrown on line 16.
The old way of implementing this would be as follows:
try
{
throw new specialException(); //line 16
throw new Exception("Weird exception");
//int a = Int32.Parse("fail");
}
catch (specialException e)
{
if(e.code != 0)
{
WriteLine("E.code isn't 0");
return;
}
throw;
}
In this case, the call stack will indicate that the exception was thrown at the throw statement, rather than on line 16.
I'll give you a good real world example that I've used it for: deadlock retry loops.
Some APIs are nice and have a specific DeadlockException sort of thing -- others, like SOAP proxies, not quite. When you don't have one, exception filters are great to avoid needing to rethrow.
int retryCount = 0;
while(true)
{
try
{
// do stuff.
break;
}
catch(Exception ex) when(ex.Message == "Deadlock" && ++retryCount < 10)
{
// retry up to 10 times.
continue;
}
}
This saves you from having to throw a wrapper exception if a non-deadlock exception happens, or if the retry limit is hit.

Repeating a function in C# until it no longer throws an exception

I've got a class that calls a SOAP interface, and gets an array of data back. However, if this request times out, it throws an exception. This is good. However, I want my program to attempt to make this call again. If it times out, I'd like it to keep making this call until it succeeds. How can I accomplish this?
For example:
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
}
catch
{
?? What Goes Here to FORCE the above line of code to rerun until it succeeds.
}
You just need to loop forever:
while (true)
{
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
break; // Exit the loop. Could return from the method, depending
// on what it does...
}
catch
{
// Log, I suspect...
}
}
Note that you should almost certainly not actually loop forever. You should almost certainly have a maximum number of attempts, and probably only catch specific exceptions. Catching all exceptions forever could be appalling... imagine if salesOrderList (unconventional method name, btw) throws ArgumentNullException because you've got a bug and filter is null... do you really want to tie up 100% of your CPU forever?
You must place the try/catch block inside a loop construct. If you wish not to consume 100% of your processor place a Thread.Sleep in the catch block, so everytime an exception occurs, it will wait some time, freeing the processor to do other things.
// iterate 100 times... not forever!
for (int i = 0; i < 100; i++)
{
try {
// do your work here;
break; // break the loop if everything is fine
} catch {
Thread.Sleep(1000);
}
}
You could also specify exception type, so that only the timeout exception is handled, and other kinds of exceptions pass-through.
// iterate 100 times... not forever!
for (int i = 0; i < 100; i++)
{
try {
// do your work here;
break; // break the loop if everything is fine
} catch (TimeOutException) {
Thread.Sleep(1000);
}
}
Note that, TimeOutException should be replaced by the real name of the exception... I don't know if that is the real name.
Also adjust the sleep time, given in millisecs and the amount of repeats, in the case I presented, 100 repeats of 1000ms yields a maximum wait of 1 minute and 40 seconds, plus the operation time itself.
If you can't change the timeout, the below should work. salesOrdersArray should be initialized to null.
while(salesOrdersArray == null)
{
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
}
catch
{
// Log failure
}
}
It its not gernally a good idead to use exceptions as control flow, but this will do what you requested.
bool Caught = true;
while (Caught)
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
Caught = false;
}
catch
{
Caught = true;
}
I will use a transactional queue (MSMQ) to store the service call. A loop will dequeue messages and call the service in a TransactionScope, if the call fails the message appear to be still in the queue. An ov erall timeout can be specified by adding a time to expire in the message. This solution is good if you really want a reliable solution since I guessed that calling that operation is critical.
Try
bool failed = false;
do {
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
}
catch
{
failed = true;
}
} while(failed);
The behavior you are after might cause an endless loop if this never succeeds though...
Try something like this:
var failed = true;
while (failed)
{
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
failed = false;
}
catch
{
}
}
Edit: Wow! Great minds think alike! :)
Although I would NOT recommend you to do this for an infinite number of times, you could make a separate function out of that one sentence:
void GoConnect()
{
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
}
catch
{
GoConnect();
}
}
while(salesOrdersArray == null){
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
}
catch(salesOrderException e)
{
log(e.message);
}
}
This will run forever, and is using exceptions as a loop which is slow. Is there a way you can modify your function that it returns null, instead of throwing an exception? If you're expecting that this call will fail regularly, don't use a try/catch block.
I follow this pattern in order to solve this problem:
public void Send(String data, Int32 attemptNumber)
{
try
{
yourCodeHere(data);
}
catch (WebException ex)
{
if (attemptNumber > 0)
Send(data, --attemptNumber);
else
throw new AttemptNumberExceededException("Attempt number exceeded!", ex);
}
catch (Exception ex)
{
//Log pourpose code goes here!
throw;
}
}
Trying forever seems not to be a good idea as you may end up having an infinite process. If you think you need many attempts to achieve your goal just set huge number here.
I personally think its wise to wait some milliseconds, or seconds after eac attempt Thread.Sleep(1000); before callig Send(data); --- you could for example, use the attempNumber variable to increse or decrease this waiting time if you think its wise for your scenario.
bool repeat = true;
while (repeat)
{
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
repeat = false;
}
catch
{
}
}

What is good C# coding style for catching SQLException and retrying

I have a method that calls a SQLServer function to perform a free text search against a table. That function will occasionally on the first call result in a SQLException: "Word breaking timed out for the full-text query string". So typically I want to retry that request because it will succeed on subsequent requests. What is good style for structuring the retry logic. At the moment I have the following:
var retryCount = 0;
var results = new List<UserSummaryDto>();
using (var ctx = new UsersDataContext(ConfigurationManager.ConnectionStrings[CONNECTION_STRING_KEY].ConnectionString))
{
for (; ; )
{
try
{
results = ctx.SearchPhoneList(value, maxRows)
.Select(user => user.ToDto())
.ToList();
break;
}
catch (SqlException)
{
retryCount++;
if (retryCount > MAX_RETRY) throw;
}
}
}
return results;
I'd change the exception handling to only retry on certain errors:
1204, 1205 deadlocks
-2 timeout
-1 connection broken
These are the basic "retryable" errors
catch (SqlException ex)
{
if !(ex.Number == 1205 || ex.Number == 1204 || ... )
{
throw
}
retryCount++;
if (retryCount > MAX_RETRY) throw;
}
Edit, I clean forgot about waits so you don't hammer the SQL box:
Add a 500 ms wait on deadlock
Add a 5 sec delay on timeout
Edit 2:
I'm a Developer DBA, don't do much C#.
My answer was to correct exception processing for the calls...
Thanks for all the feedback. I'm answering this myself so I can incorporate elements from the answers given. Please let me know if I've missed something. My method becomes:
var results = new List<UserSummaryDto>();
Retry<UsersDataContext>(ctx => results = ctx.SearchPhoneList(value, maxRows)
.Select(user => user.ToDto())
.ToList());
return results;
And I've refactored the original method for reuse. Still lots of levels of nesting. It also relies on there being a default constructor for the data context which may be too restrictive. #Martin, I considered including your PreserveStackTrace method but in this case I don't think it really adds enough value - good to know for future reference thanks:
private const int MAX_RETRY = 2;
private const double LONG_WAIT_SECONDS = 5;
private const double SHORT_WAIT_SECONDS = 0.5;
private static readonly TimeSpan longWait = TimeSpan.FromSeconds(LONG_WAIT_SECONDS);
private static readonly TimeSpan shortWait = TimeSpan.FromSeconds(SHORT_WAIT_SECONDS);
private enum RetryableSqlErrors
{
Timeout = -2,
NoLock = 1204,
Deadlock = 1205,
WordbreakerTimeout = 30053,
}
private void Retry<T>(Action<T> retryAction) where T : DataContext, new()
{
var retryCount = 0;
using (var ctx = new T())
{
for (;;)
{
try
{
retryAction(ctx);
break;
}
catch (SqlException ex)
when (ex.Number == (int) RetryableSqlErrors.Timeout &&
retryCount < MAX_RETRY)
{
Thread.Sleep(longWait);
}
catch (SqlException ex)
when (Enum.IsDefined(typeof(RetryableSqlErrors), ex.Number) &&
retryCount < MAX_RETRY)
{
Thread.Sleep(shortWait);
}
retryCount++;
}
}
}
My enum of retryables for sql looks like this:
SqlConnectionBroken = -1,
SqlTimeout = -2,
SqlOutOfMemory = 701,
SqlOutOfLocks = 1204,
SqlDeadlockVictim = 1205,
SqlLockRequestTimeout = 1222,
SqlTimeoutWaitingForMemoryResource = 8645,
SqlLowMemoryCondition = 8651,
SqlWordbreakerTimeout = 30053
It's not good style, but sometimes you have to do it, because you simply can't change existing code and have to deal with it.
I am using the following generic method for this scenario. Note the PreserveStackTrace() method, which can sometimes be very helpful in a re-throw scenario.
public static void RetryBeforeThrow<T>(Action action, int retries, int timeout) where T : Exception
{
if (action == null)
throw new ArgumentNullException("action", string.Format("Argument '{0}' cannot be null.", "action"));
int tries = 1;
do
{
try
{
action();
return;
}
catch (T ex)
{
if (retries <= 0)
{
PreserveStackTrace(ex);
throw;
}
Thread.Sleep(timeout);
}
}
while (tries++ < retries);
}
/// <summary>
/// Sets a flag on an <see cref="T:System.Exception"/> so that all the stack trace information is preserved
/// when the exception is re-thrown.
/// </summary>
/// <remarks>This is useful because "throw" removes information, such as the original stack frame.</remarks>
/// <see href="http://weblogs.asp.net/fmarguerie/archive/2008/01/02/rethrowing-exceptions-and-preserving-the-full-call-stack-trace.aspx"/>
public static void PreserveStackTrace(Exception ex)
{
MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic);
preserveStackTrace.Invoke(ex, null);
}
You would call it like that:
RetryBeforeThrow<SqlException>(() => MethodWhichFails(), 3, 100);
There is no good style for doing something like this. You'd be better off figuring out why the request fails the first time but succeeds the second time.
It seems possible that Sql Server has to initially compile an execution plan and then execute the query. So the first call fails because the combined times exceed your timeout property, and succeeds the second time because the execution plan is already compiled and saved.
I don't know how UsersDataContext works, but it may be the case that you have the option to Prepare the query before actually executing it.
Real Answer: If I had to do this, I would retry just once and not again, like this:
var results = new List<UserSummaryDto>();
using (var ctx = new
UsersDataContext(ConfigurationManager.ConnectionStrings[CONNECTION_STRING_KEY].ConnectionString))
{
try
{
results = ctx.SearchPhoneList(value, maxRows)
.Select(user => user.ToDto())
.ToList();
break;
}
catch (SqlException)
{
try
{
results = ctx.SearchPhoneList(value, maxRows)
.Select(user => user.ToDto())
.ToList();
break;
}
catch (SqlException)
{
// set return value, or indicate failure to user however
}
}
}
}
return results;
While I might trust you to not abuse the retry process, you'd be tempting your successor to increase the retry count as a quick fix.
I think annotating a method with an aspect specifying the retry count would result in more structured code, although it needs some infrastructure coding.
You can simply use SqlConnectionStringBuilder properties to sql connection retry.
var conBuilder = new SqlConnectionStringBuilder("Server=.;Database=xxxx;Trusted_Connection=True;MultipleActiveResultSets=true");
conBuilder.ConnectTimeout = 90;
conBuilder.ConnectRetryInterval = 15;
conBuilder.ConnectRetryCount = 6;
Note:- Required .Net 4.5 or later.
Pull the relevant code out into its own method, then use recursion.
Pseudo-code:
try
{
doDatabaseCall();
}
catch (exception e)
{
//Check exception object to confirm its the error you've been experiencing as opposed to the server being offline.
doDatabaseCall();
}

Categories

Resources