Try/Catch exception continue from line that caused the exception - c#

When an exception is thrown, how can I catch it and then continue execution starting from the line that caused the error?
EDIT:
Our program communicates with Indesign Server which crashes all the time and throws random COM related errors (these errors have to do with bugs in the Server itself). Indesign Server also takes a very long time to process commands so when it crashes, we want to avoid restarting execution. Instead, we want to continue from the line that caused the exception. Any line in the program can cause an exception. So technically, we cannot use a loop.

When an exception is thrown, how can I catch it and then continue execution starting from the line that caused the error? (Not the next line; retry the line that caused the exception.)
Do not try to do that. You're approaching this problem from the wrong direction.
The problem is that you have an unreliable subsystem. You have a desired policy for dealing with that unreliable subsystem, which is to retry the operation until it succeeds. If that's the case then don't put that logic in the line-of-business code which uses the subsystem. The line-of-business code should be about the business logic, not about the mechanism you choose to deal with the flaky subsystem. Isolate the mechanism to a specific class which makes the unreliable subsystem into a reliable subsystem.
That is, build a proxy class that has the same interface as the unreliable subsystem, and isolate your retry logic into that proxy class. Then the line-of-business code can use the proxy class as a reliable subsystem.
That said, a policy of "retry it until it works" is possibly a bad policy. If the subsystem is genuinely broken and not just flaky in some transient way, then "retry until it works" means "wait forever", and most users do not like waiting forever. For example, if the exception is a result of a router being unplugged rather than some transient condition then sitting there in a loop until someone plugs the router back in seems like a bad idea.

If you're looking for something general purpose then using a lambda would do the trick. For example
public static class Exception {
public static void Continue(Action action) {
try {
action();
} catch {
// Log
}
}
}
Exception.Continue(() => Statement1());
Exception.Continue(() => Statement2());
I wouldn't consider this an ideal solution though for large scale use. It causes an extra delegate allocation, delegate invocation and method invocation for every statement you use this on. Instead I would focus on identifying the functions which are causing you problems and add explicit wrappers for them individually.

You would have to surround any line that could throw an exception in its own try/catch block to accomplish that.
So instead of
try
{
StatementOne(); // Exception thrown here
StatementTwo();
}
catch (SOneException) { ... }
You would have to do:
try
{
StatementOne();
}
catch (SOneException) { ... }
StatementTwo();
If you need to retry an operation due to a (hopefully transient) exception, you can have a method like this:
public static class ExceptionHelper
{
public static void TryNTimesAndThenThrow(Action statement, int retryCount)
{
bool keepTrying = false;
do
{
try
{
statement();
keepTrying = false;
}
catch (Exception)
{
if (retryCount > 0)
{
keepTrying = true;
retryCount--;
}
else
{
// If it doesn't work here, assume it's broken and rethrow
throw;
}
}
} while (keepTrying)
}
}
Then you can just write:
ExceptionHelper.TryNTimesAndThenThrow(() => MightThrowATransientException(), 3);
Keep in mind both methods should be used sparingly. The former will clutter your code quite a bit, while the latter could end up taking a lot more time than you think (since its often a better idea to simply alert the user if something unexpected occurs. Thus the emphasis on a transient exception that you really do expect will disappear if you just try again.)

You could do something like this:
//Retry logic on opening the connection
int retries = 0;
openconnection:
try
{
connection.Open();
}
catch
{
retries++;
//Wait 2 seconds
System.Threading.Thread.Sleep(2000);
if (retries < MAXRETRIES)
{
goto openconnection;
}
else
{
throw;
}
}

I do not know about c#, but this is a javascript that can make a script run multiple times if there is an error.
try {
for (var i=0; (i < t && condition) || i === 0; i++) {
//t is how many times you want the code to run.
//condition should be true when there is an error.
//The code you want to execute multiple times if it fails.
}
} catch (e) {
//The code you want to execute if the code still runs into an error after being repeated multiple times.
}
For example, the following code simulates the case where you set a variable equal to the response from another server. And, the server responds on the 6th time the script is run.
try {
for (var i = 0; (i < 10 && typeof response === "undefined") || i === 0; i++) {
var response = (i === 5) ? 1 : undefined;
if (typeof response === "undefined") {console.log("ERROR!! #" + i)} else {console.log("Success!! #" + i)};
if (i === 9 && typeof response === "undefined") {throw new Error("Fail to get response from other server")};
}
} catch (e) {
console.log("Error Message: '" + e + "'");
}
You can run the above code to see the effect. The following is when another server never reply.
try {
for (var i = 0; (i < 10 && typeof response === "undefined") || i === 0; i++) {
var response = (i === -1) ? 1 : undefined;
if (typeof response === "undefined") {console.log("ERROR!! #" + i)} else {console.log("Success!! #" + i)};
if (i === 9 && typeof response === "undefined") {throw new Error("Fail to get response from other server")};
}
} catch (e) {
console.log("Error Message: '" + e + "'");
}
You can also make it into a function to use it easily.
function retry(code,times,condition,errorMessage) {
try {
for (var i = 0; (i < times && eval(condition)) || i === 0; i++) {
eval(code);
if (i === times-1 && eval(condition) && typeof errorMessage !== "undefined") {throw new Error(errorMessage)};
}
} catch (e) {
console.log("Error Message: '" + e + "'");
}
}
The first two example using the function.
function retry(code,times,condition,errorMessage) {
try {
for (var i = 0; (i < times && eval(condition)) || i === 0; i++) {
eval(code);
if (eval(condition)) {console.log("ERROR!! #" + i)} else {console.log("Success!! #" + i)};
if (i === times-1 && eval(condition) && typeof errorMessage !== "undefined") {throw new Error(errorMessage)};
}
} catch (e) {
console.log("Error Message: '" + e + "'");
}
}
retry("var response = (i === 5) ? 1 : undefined;",10,"typeof response === 'undefined'","Fail to get response from other server")
retry("var response = (i === -1) ? 1 : undefined;",10,"typeof response === 'undefined'","Fail to get response from other server")
I hope this helps some people.

Related

Request timeout handling c# winforms

I need to know how to catch and recognize timeout error in comparison to other WebException errors. Request timeout is set to "1" to make environment to be able to catch the exception. I just need to know how to recognize it. (i.e. default working value = 60000). Here is my code:
// some code here
request.Timeout = 1;
// some code here
catch (WebException wex)
{
Console.WriteLine(wex);
try
{
response_code = ((int)((HttpWebResponse)wex.Response).StatusCode);
State_show.ForeColor = System.Drawing.Color.Red;
if (response_code == 404)
{
State_show.Text = "Error 404. Retrying the request";
request_1();
}
if (response_code != 400 || response_code != 503 || response_code != 404)
{
State_show.Text = "Error " + response_code + ". Please try again";
FlashWindow.Flash(this);
}
}
catch (Exception exc)
{
Console.WriteLine(exc);
MessageBox.Show("Check internet connection");
}
}
So it catches good if I received bad http status code. But it throws additional exception if response has timed out. The simplest way is to get
string wex_modified = wex.ToString();
If (wex_modified.contains("Timeout"))
{
// some handling here
}
But I don't really like it. I tried to use wex.GetType() and other available functions, but without success.
Is there any other way to recognize the exception?
The WebException.Status property returns a WebExceptionStatus enum. One of the enumeration values is Timeout.
if (wex.Status == WebExceptionStatus.Timeout)
{
// We have a timeout!
}

Getting AccessViolationException on client PCs

Does anyone have experience using EZTwain's BARCODE_Recognize function?
I'm wondering why I'm getting an AccessViolationException in my program when trying to use the Dosadi EZTwain library to recognize bar codes from scanned images.
This program runs flawlessly on our dev environment, so I'm having a hard time tracking down what exactly is the cause of the problem when it runs on the client's PCs.
I've found through using the ildasm.exe program that the exception originates in this method of my barcode recognition utility class.
Edit: I'm wondering if it could have anything to do with our users not being set up as administrators? Since we have no problems at all running it here as admin, then they get this exception the first time it calls this GetBarcode method?
Edit: Is there anything else anyone else would need to see from my code to help me track down this problem?
The ildasm where the error occurs looks like this. The error is on IL_00bb: ldloc.3 which is the first parameter "image".
//000053: count = EZTwain.BARCODE_Recognize(image, -1, -1);
IL_00bb: ldloc.3
IL_00bc: ldc.i4.m1
IL_00bd: ldc.i4.m1
IL_00be: call int32 Dosadi.EZTwain.EZTwain/*02000005*/::BARCODE_Recognize(native int,
int32,
int32) /* 060001E4 */
IL_00c3: stloc.1
GetBarcode
public static string GetBarcode(Bitmap bImage, out BarcodeType barcodeType)
{
barcodeType = BarcodeType.NotBarcode;
string selectedBarcode = null;
int count = 0;
IntPtr hImage = IntPtr.Zero, image = IntPtr.Zero;
List<string> barcodes = new List<string>();
try
{
try
{
if (bImage.Width == 0 || bImage.Height == 0)
{
return null;
}
hImage = bImage.GetHbitmap();
if (hImage == IntPtr.Zero)
{
return null;
}
image = EZTwain.DIB_FromBitmap(hImage, IntPtr.Zero);
}
catch (Exception ex)
{
logger.LogException(LogLevel.Debug,
"Exception in GetBarcode(): inner try-catch block", ex);
throw;
}
finally
{
if (hImage != IntPtr.Zero)
DeleteObject(hImage);
}
EZTwain.BARCODE_SetDirectionFlags(-1);
count = EZTwain.BARCODE_Recognize(image, -1, -1);
UtilDebug("Found {0} barcodes in image on first attempt.", count);
for (int i = 0; i < count; i++)
{
barcodes.Add(EZTwain.BARCODE_Text(i));
}
foreach (string code in barcodes)
{
UtilDebug("Processing barcode \"{0}\".", code);
if (ProcessBarcodeType(code) == BarcodeType.CoversheetBarcode || ProcessBarcodeType(code) == BarcodeType.RegularBarcode)
{
barcodeType = ProcessBarcodeType(code);
selectedBarcode = code;
UtilDebug("SelectedBarcode set to \"{0}\".", code);
break;
}
}
}
catch (Exception ex)
{
logger.LogException(LogLevel.Debug, "Exception in GetBarcode(): outer try-catch block", ex);
throw;
}
finally
{
if (image != IntPtr.Zero)
EZTwain.DIB_Free(image);
barcodes.Clear();
}
//Find one that is an ASI barcode before return
return selectedBarcode;
}
The way you're interacting with the barcode engine looks OK to me. I would guess maybe the barcode engine is choking on something internally, maybe there's something about that image.
BTW I think newer editions of the EZTwain toolkit include a function that does that conversion from System.Drawing.Bitmap to EZTwain's 'HDIB'... yes: DIB_FromImage. It works differently from your code, might be worth trying.
I would start by collecting a log (using DosadiLog, an app in your Start menu under EZTwain) on the failing machine, collected while reproducing the crash. Either post it here or - probably more legit, open an issue on the Atalasoft Support Portal and attach it to that.
Sorry it took us so long to notice your post here, we'll set up better monitoring now.

Best way to handle a WCF timeout

I have a real time app that tracks assets around a number of sites across the country. As part of this solution I have 8 client apps that update a central server.
My question is that sometimes the apps lose connection to the central server and I am wondering what is the best way to deal with this ? I know I could just increase the max send/receive times to deal with the timeout BUT I also want a graceful solution to deal with if the connection to the server is down:
For example I'm calling my services like this :
using (var statusRepository = new StatusRepositoryClient.StatusRepositoryClient())
{
statusId = statusRepository.GetIdByName(licencePlateSeen.CameraId.ToString());
}
I was thinking of adding a try/catch so...
using (var statusRepository = new StatusRepositoryClient.StatusRepositoryClient())
{
try
{
statusId = statusRepository.GetIdByName(licencePlateSeen.CameraId.ToString());
}
catch (TimeoutException timeout)
{
LogMessage(timeout);
}
catch (CommunicationException comm)
{
LogMessage(comm);
}
}
Dealing it this way doesn't allow me to rerun the code without having a ton of code repeat. Any one got any suggestions ?
EDIT: Looking into Sixto Saez and user24601 answers having an overall solution is better than dealing with timeouts on an individual exception level BUT... I'm was thinking that the below would solve my problem (but it would add a TON of extra code error handling):
void Method(int statusId)
{
var statusRepository = new StatusRepositoryClient.StatusRepositoryClient()
try
{
IsServerUp();
statusId = statusRepository.GetIdByName(licencePlateSeen.CameraId.ToString());
statusRepository.Close();
}
catch (Exception ex)
{
statusRepository.Abort();
if (ex is TimeoutException || ex is CommunicationException)
{
LogMessage(timeout);
Method(statusId);
}
else
{
throw new Exception(ex.Message + ex.InnerException);
}
}
}
}
bool IsServerUp()
{
var x = new Ping();
var reply = x.Send(IPAddress.Parse("127.0.0.1"));
if (reply == null)
{
IsServerUp();
}
else
{
if (reply.Status != IPStatus.Success)
{
IsServerUp();
}
}
return true;
}
For starters I think your Wcf error handling is wrong. It should look like this
var statusRepository = new StatusRepositoryClient.StatusRepositoryClient();
try
{
statusId = statusRepository.GetIdByName(licencePlateSeen.CameraId.ToString());
statusRepository.Close()
}
catch(Exception e)
{
statusRepository.Abort();
LogMessage(e);
throw; //I would do this to let user know.
}
I would also re-throw the error to let the user know about the problem.
Prior to designing your exception handling, one important decision to make is whether you want guaranteed delivery of each message the client sends or is it OK for the service to "lose" some. For guaranteed delivery, the best built-in solution is the netMsmqBinding assuming the client can be configured to support it. Otherwise, there is a lightweight reliable messaging capability built into WCF. You'll be going down a rabbit hole if you try to handle message delivery purely through exception handling... :)
I have a two-pronged approach to verifying the server is up:
1) I have set up a 'PING' to the server every 5 seconds. The server responds with a 'PONG' and a load rating (low, medium, high, so the client can adjust its load on the server). If the client EVER doesn't receive a pong it assumes the server is down (since this is very low stress on the server - just listen and respond).
2) Random timeouts like the one you are catching are logged in a ConnectionMonitor class along with all successful connections. A single one of these calls timing out is not enough to consider the server down since some may be very processor heavy, or may just take a very long time. However, a high enough percentage of these will cause the application to go into server timeout.
I also didn't want to throw up a message for every single connection timeout, because it was happening too frequently to people who use poorer servers (or just some computer lying in their lab as a server). Most of my calls can be missed once or twice, but missing 5 or 6 calls are clearly going to cause instrusion.
When a server-timeout state happens, I throw up a little dialog box explaining what's happening to the user.
Hi Please see my solution below. Also please note that the below code has not been compliled so may have some logic and typing errors.
bool IsServerUp()
{
var x = new Ping();
var reply = x.Send(IPAddress.Parse("127.0.0.1"));
if (reply == null) return false;
return reply.Status == IPStatus.Success ? true : false;
}
int? GetStatusId()
{
try
{
using (var statusRepository = new StatusRepositoryClient.StatusRepositoryClient())
{
return statusRepository.GetIdByName(licencePlateSeen.CameraId.ToString());
}
}catch(TimeoutException te)
{
//Log TimeOutException occured
return null;
}
}
void GetStatus()
{
try
{
TimeSpan sleepTime = new TimeSpan(0,0,5);
int maxRetries = 10;
while(!IsServerUp())
{
System.Threading.Thead.Sleep(sleepTime);
}
int? statusId = null;
int retryCount = 0;
while (!statusId.HasValue)
{
statusId = GetStatusId();
retryCount++;
if (retryCount > maxRetries)
throw new ApplicationException(String.Format("{0} Maximum Retries reached in order to get StatusId", maxRetries));
System.Threading.Thead.Sleep(sleepTime);
}
}catch(Exception ex)
{
//Log Exception Occured
}
}

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();
}

How can I improve this exception retry scenario?

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.

Categories

Resources