Attempt and retry - c#

How can I formalize this to be more generic, where I can specify an X exceptions to throw and X exceptions to try again all while improving the code readability.
private const int RetrySegmentCount = 3;
private const int SecondsBetweenRetry = 30;
var retryCounter = 0;
while (true)
{
try
{
ExecuteProcessThatMayThrow();
break;
}
catch (NotSupportedException) // Do no retry if this is thrown
{
throw;
}
catch (Exception)
{
if (retryCounter < RetrySegmentCount)
{
retryCounter++;
Thread.Sleep(SecondsBetweenRetry * 1000);
}
else
{
throw;
}
}
}
An ideal syntax in puesdocode might be
Repeat(3, 30, [NotSupportedException], [Exception]) => ExecuteProcessThatMayThrow();
Repeat(3, 30) => ExecuteProcessThatMayThrow(); // This will repeat on all
Repeat(3, 30, [NotSupportedException, VeryBadException], [RetryableException]) => ExecuteProcessThatMayThrow();

You can create a reusable method that has multiple result depending on the error type. Here a small modified version of what i use
This method handles the different conditions and retry
public static bool TryExecute(Action action, int retry, int secondBeforeRetry, List<Type> notSupportedExceptions, List<Type> veryBadExceptions, List<Type> retryableExceptions)
{
var success = false;
// keep trying to run the action
for (int i = 0; i < retry; i++)
{
try
{
// run action
action.Invoke();
// if it reached here it was successful
success = true;
// break the loop
break;
}
catch (Exception ex)
{
// if the exception is not retryable
if (!retryableExceptions.Contains(ex.GetType()))
{
// if its a not supported exception
if (notSupportedExceptions.Contains(ex.GetType()))
{
throw new Exception("No supported");
}
else if (veryBadExceptions.Contains(ex.GetType()))
{
throw new Exception("Very bad");
}
}
else
{
System.Threading.Thread.Sleep(secondBeforeRetry * 1000);
}
}
}
return success;
}
To call this method it before very easy as they can all be easily change to optional parameters. here is and example :
// sample action that force an error to be thrown
var a = new Action(() =>
{
var test = "";
var test2 = test[3]; // throw out of range exception
});
try
{
var success = TryExecute(a, 5, 30, new List<Type>() { typeof(IndexOutOfRangeException) }, new List<Type>(), new List<Type>());
}
catch (Exception ex)
{
// handle whatever you want
}

Related

Assign passed functions result to object with variable type in C#

For an integration I'm running as a service once a day, I need to assign the result of API-calls to local variables. However, those API's might at any time decide to throw a 401 error, in which case I just want to try again, up to three times.
I've got a functioning code to do that:
List<APIEntityProject> projectList = null;
private bool SetProjectList(){
const maxRetries = 3;
const RetryPause = 3000;
int retries = 0;
do
{
try
{
projectList = ProjApi.GetProject(activeWorkspace.WorkspaceCode);
}
catch (ApiException e)
{
if (e.ErrorCode == 401) // Unauthorized error (e.g. user doesn't have access to this Workspace
{
Log.Warning("Unauthorized error while fetching projects from Workspace, try {retries}",retries);
retries++;
System.Threading.Thread.Sleep(RetryPause * retries);//Waits 3 and then 6 seconds before retrying.
}
else throw;
}
} while (projectList == null || retries < maxRetries);
if (retries == maxRetries)
{
Log.Error("An error has occured while trying to retrieve affected Projects, skipped document");
errorCount++;
return false;
}
return true;
}
But unfortunately I need to replicate this Logic so often I would like to use it in a function e.g. RetryNTimes (similar to This Solution
List<APIEntityProject> projectList = null;
List<APIEntityWBS> WBSList = null;
List<APIEntitySopeItem> SIList = null;
List<APIEntityScopeAsignment> SAList = null;
List<APIEntityActivity> ActList = null;
...
RetryNTimes(projectList,ProjApi.GetProject(activeWorkspace.WorkspaceCode),3,3000,"ProjectList");
RetryNTimes(WBSList, WBSApi.GetAllWBS(activeProject.ProjectID),3,3000,"WBSList");
RetryNTimes(SIList, SIApi.GetAllScopeItems(activeProject.ProjectID),3,3000,"ScopeItemsList");
RetryNTimes(SAList, SAApi.GetAllScopeAssignments(activeProject.ProjectID),3,3000,"ScopeAssignmentsList");
RetryNTimes(ActList, ActApi.GetAllActivities(activeProject.ProjectID),3,3000,"ActivityList");
...
private bool RetryNTimes(T object, Func<T> func, int times, int WaitInterval, string etext){
do
{
try
{
object = func();
}
catch (ApiException e)
{
if (e.ErrorCode == 401)
{
retries++;
Log.Warning("Unauthorized error while fetching {APIErrorSubject}, try {retries}",eText,retries);
System.Threading.Thread.Sleep(RetryPause * retries);//Waits 3 and then 6 seconds before retrying.
}
else throw;
}
} while (object == null || retries < maxRetries);
if (retries == maxRetries)
{
Log.Error("An error has occured while trying to retrieve {APIErrorSubject}, skipped document",eText);
errorCount++;
return false;
}
return true;
}
I've also read through typedef and function pointers but I'm not sure if it's possible to do with variable types.
Any Ideas?
That article refers to C language. In C# you can use delegates. Here's a link to start you off.
Based on the idea of asawyer and by looking through some other examples of delegates I've been able to make it work.
static T2 TryNTimes<T1,T2>(Func<T1,T2> func,T1 obj, int times, int WaitInterval)
{
while (times > 0)
{
try
{
T2 result = func.Invoke(obj);
return result;
}
catch (Exception e)
{
if (--times <= 0)
throw;
System.Threading.Thread.Sleep(WaitInterval * times);
}
}
return default;
}
Now I need only 2 steps in my main function
activeWorkspace = TryNTimes(WrkApi.WorkspaceCodeWorkspaceCodeFindByName17, ServiceSettings.sqlConnection.Workspace, 3, 3000)[0];
ProjectList = TryNTimes(WrkApi.GetProjectsByWorkspaceCode, activeWorkspace.code, 3, 3000);
The first one can still generate an error as the default List is empty and you can't take 0th element then. But I guess I can find another way around that issue.

Execute multiple functions, but stop when an error occured

I want to use multiple functions after each other but if something went wrong in the first function the other functions shouldn't be executed. At the moment I'm using a while loop with a switch. Is there a way to skip the whole while/switch part with something else? Maybe something like an event?
while (!ErrorActive && iStep != 3)
{
switch (iStep)
{
case 0:
DoSomething(); // this can trigger ErrorActive
iStep = 1;
break;
case 1:
DoSomething2(); // this can trigger ErrorActive
iStep = 2;
break;
case 2:
DoSomething3(); // this can trigger ErrorActive
iStep = 3;
break;
}
}
the DoSomething functions have something like this:
public void DoSomething()
{
try
{
//calculate something
}
catch
{
ErrorActive = true;
}
}
Is there a way to skip the whole while/switch part and replace it with something else (like an event maybe?) or should I always keep something in between each function to check if everything is all right?
Just move the catch one level up:
// true if all steps executed, false otherwise
bool DoSteps()
{
int lastExecutedStep = 0;
try{
DoSomething();
lastExecutedStep = 1;
DoSomething1();
lastExecutedStep = 2;
DoSomething2();
lastExecutedStep = 3;
}
catch( IOException ioex )
{
// log IO Exception
}
// ... catch more expected exception types
return (lastExecutedStep == 3);
}
void DoSomething(){
// NO try/catch here
}
Even possible without stepcounter:
// true if all steps executed, false otherwise
bool DoSteps()
{
try{
DoSomething();
DoSomething1();
DoSomething2();
return true;
}
catch( IOException ioex )
{
// log IO Exception
}
// ... catch more expected exception types
return false;
}
For a more academical approach you may want to explore Chain of responsibility pattern
You can do as follows , why you use so mach code when solution is very simple.
if(!ErrorActive){
DoSomething(); // this can trigger ErrorActive
}
if(!ErrorActive){
DoSomething1(); // this can trigger ErrorActive
}
if(!ErrorActive){
DoSomething2(); // this can trigger ErrorActive
}
You can simplify the while loop to a for loop and make use of the new switch expression syntax to simplify things a bit. Also, have the functions return a success state rather than setting some shared variable:
void Run()
{
var keepRunning = true;
for(int i = 0; keepRunning; i++)
{
keepRunning = i switch
{
0 => DoSomething(),
1 => DoSomething2(),
2 => DoSomething3(),
_ => false
};
}
}
bool DoSomething()
{
try
{
return true;
}
catch
{
return false;
}
}
bool DoSomething2()
{
try
{
return true;
}
catch
{
return false;
}
}
bool DoSomething3()
{
try
{
return true;
}
catch
{
return false;
}
}

using do-while and skipping the next lines

I want to run a method _doing() that loops infinitely until a shutdownEvent is triggered. This is basically executed/started on a new Thread(). But I need not _doSomething if it is true somewhere. what should i do after if (_doSomething)? Code snippet below. Thanks.
private HighResolutionLapseTimer _lapseTimer;
private int _timeToNext
{
get
{
int lapseTime = _lapseTimer.LapseTime();
int next = DO_PERIOD - lapseTime;
if (next > 0)
{
return next;
}
else
{
return 1;
}
}
}
int DO_PERIOD = 60000;
private void _doing()
{
int _nextDoing = DO_PERIOD;
Thread.Sleep(_nextDoing);
do
{
LogInfo("Starting _doing");
lock (this)
{
if (_doSomething)
{
// skip this time because we are currently archiving
}
_doSomething = true;
}
try
{
_lapseTimer.Start();
DoSomethingHere(); //takes long processing
}
catch (Exception ex)
{
LogException(ex);
}
finally
{
lock (this)
{
_nextDoing = (int)_timeToNext;
_doSomething = false;
}
}
} while (!shutdownWaitHandle.WaitOne(_nextDoing, false));
LogInfo("Stopping _doing");
}
You could use the continue; statement.
Ohw! I just realized that a do-while is similar to a while. And to 'skip' the execution, you just have to use the continue; if the if statement is true

How can I pass variable to Action?

below is the code sample, I want the components in the queue to take the local variable "entity", how can I achive this? thx
private void DoComparison(StuffEntity entity)
{
try
{
bool dataFlag = CheckIsNewData(entity.PickingTime, entity.WarningPeriod);
if (dataFlag)
{
Queue<Action<StuffEntity>> queue = new Queue<Action<StuffEntity>>();
//How can I let the queue stuff take the entity?
queue.Enqueue(DelaySendingMessageOut);
if (!QueueItem.ContainsKey(entity.FridgeID))
{
QueueItem.Add(entity.FridgeID, queue);
}
}
}
catch (Exception ex)
{
CommonUnity.WriteLog(ex.Message);
CommonUnity.WriteLog(ex.StackTrace);
}
}
private void DelaySendingMessageOut(StuffEntity entity)
{
int pendingPeroid = entity.PendingTime.ToInt();
if (pendingPeroid <= 0)
pendingPeroid = 5;
Thread.Sleep(pendingPeroid * 60 * 1000);
TriggerCheckingBeforeSendMessageOut(entity);
}
queue.Enqueue((e) => DelaySendingMessageOut(entity));
But you can use Queue<Action> since you will not use the argument e:
Queue<Action> queue = new Queue<Action>();
queue.Enqueue(() => DelaySendingMessageOut(entity));

The best way (pattern) to execute something while exception is thown

Below is a code showing tries to encapsulate a logic to re-run something while an exception is being catch.
Does exist patterns or something else to do that ? Or what improvements would you suggest to that code ?
public static void DoWhileFailing(int triesAmount, int pauseAmongTries, Action codeToTryRun) {
bool passed = false;
Exception lastException = null;
for (int i = 0; !passed && i < triesAmount; i++) {
try {
if (i > 0) {
Thread.Sleep(pauseAmongTries);
}
codeToTryRun();
passed = true;
} catch(Exception e) {
lastException = e;
}
}
if (!passed && lastException != null) {
throw new Exception(String.Format("Something failed more than {0} times. That is the last exception catched.", triesAmount), lastException);
}
}
I would re-write this to eliminate a few variables, but in general your code is OK:
public static void DoWhileFailing(int triesAmount, int pauseAmongTries, Action codeToTryRun) {
if (triesAmount<= 0) {
throw new ArgumentException("triesAmount");
}
Exception ex = null;
for (int i = 0; i < triesAmount; i++) {
try {
codeToTryRun();
return;
} catch(Exception e) {
ex = e;
}
Thread.Sleep(pauseAmongTries);
}
throw new Exception(String.Format("Something failed more than {0} times. That is the last exception catched.", triesAmount, ex);
}
I wrote the following code to do basically the same thing. It also lets you specifiy the type of Exception to catch and a Func that determines if the current iteration should throw the exception or continue retrying.
public static void RetryBeforeThrow<T>(
this Action action,
Func<T, int, bool> shouldThrow,
int waitTime) where T : Exception
{
if (action == null)
throw new ArgumentNullException("action");
if (shouldThrow == null)
throw new ArgumentNullException("shouldThrow");
if (waitTime <= 0)
throw new ArgumentException("Should be greater than zero.", "waitTime");
int tries = 0;
do
{
try
{
action();
return;
}
catch (T ex)
{
if (shouldThrow(ex, ++tries))
throw;
Thread.Sleep(waitTime);
}
}
while (true);
}
Then you can call it like this
Action a = () =>
{
//do stuff
};
a.RetryBeforeThrow<Exception>((e, i) => i >= 5, 1000);
And you can specify any exception type and you can check the exception in the Func to determine if it is an exception that you want to throw or to continue to retry. This gives you the ability to throw your own exceptions in your Action that will stop the retries from occurring.
I don't see anything wrong with the code, I would just question your assumptions.
A couple of problems that I see:
Clients need to understand the failure modes of the called action to choose the right parameters.
The action could fail intermittently, which is a capacity killer. Code like this can't scale well.
Clients can wait for an indeterminate amount of time for the action to complete.
All exceptions but the last will be swallowed, which could hide important diagnostic information.
Depending on your needs, your code might suffice, but for a more robust way to encapsulate an unreliable resource, take a look at the circuit breaker pattern.

Categories