How to throw final error using WaitAndRetry and Execute? - c#

I'm, trying to check a simple WaitAndRetry of Polly
class Program
{
public static void Main()
{
int i = 0;
var _retryPolicy = Policy
.Handle<Exception>()
.WaitAndRetry(Backoff.ExponentialBackoff(TimeSpan.FromSeconds(2), 10),
(exception, timespan) =>
{
Console.WriteLine($"Retry: {timespan}. \n ex: {exception}");
});
_retryPolicy.Execute(() =>
{
Console.WriteLine(i);
i++;
int.Parse("something");
});
Console.ReadLine();
}
}
And I want to throw a final exception after all the retries are failed. How can I do it?
Excepted Result:
Retry: ..
Retry: ..
Retry: ..
My new final error!
Thank You!

You don't have to explicitly re-throw the last exception.
The retry works in the following way:
If an exception is thrown by the decorated method and there is a related .Handle<TEx> or .Or<TEx> clause inside the policy definition then it checks if the retry limit has been reached or not
If the retry limit has not been exceeded then it will perform yet another retry attempt
If the retry limit has been reached then it will throw the last exception
If there is not related .Handle<TEx> or .Or<TEx> clause then it will throw the last exception
Here is a diagram from the official documentation
Please also note that if you run your application in a debug mode then the IDE might stop each time when the decorated method throws exception. It depends on your IDE settings.

Related

Is it possible to unit test CallActivityWithRetryAsync and reproduce retries?

I have read the Microsoft Documentation but it doesn't mention CallActivityWithRetryAsync.
My IDurableOrchestrationContext is mocked for the call I'm making:
mockContext.Setup(c => c.CallActivityWithRetryAsync(nameof(SerialiseXml), It.IsAny<RetryOptions>(),
It.IsAny<InboundOrchestrationData>()))
.ThrowsAsync(new IOException());
and I can put a breakpoint at the calling point:
try
{
await context.CallActivityWithRetryAsync("SerialiseXml",
new RetryOptions(TimeSpan.FromMinutes(1), 3), data); // breakpoint
}
catch (Exception e)
{
log.LogError("Problem serialising xml.");
}
but my code only ever breaks here once when I would expect it to break three times according to the RetryOptions.
If I pass in a Mock<ILogger<Class>> and check the invocations there is also only one. I have also configured my mock using SetupSequence() and multiple .ThrowsAsync.
Do mind that if you start mocking
mockContext.Setup(c => c.CallActivityWithRetryAsync(nameof(SerialiseXml), It.IsAny<RetryOptions>(),
It.IsAny<InboundOrchestrationData>()))
.ThrowsAsync(new IOException());
there won't be any retries performed at all since it is mocked. There is no real implementation that is executed.
Inside the implementation of CallActivityWithRetryAsync there will be a retry of the specified activity call in case of a retryable failure. Only after all retry attempts have failed an exception will bubble up to your code. So I ever expect just one call to CallActivityWithRetryAsync.
Your exception handling code will also be just called once, and only when all retries have failed or there is another reason for the call to fail.
The implementation of CallActivityWithRetryAsync will look like this (simplified):
async Task<int> CallActivityWithRetryAsync(string activity, RetryOptions retryOptions)
{
int attempts = 1;
while (true)
{
try
{
return await CallActivity(activity);
}
catch
{
++attempts;
if (attempts > retryOptions.RetryCount)
{
throw;
}
}
}
}
Given this pseudocode you can easily see why the breakpoint hits only one.

How to throw a custom exception in polly rate limit exceeds the rate?

I have an API that has certain limits defined. Since I have used Polly C# library to limit the calls made to API. Below is the policy I am using.
var _rateLimitPolicy = Policy.RateLimitAsync(10,TimeSpan.FromSeconds(1), 5);
var _retryPolicy = Policy
.Handle<RateLimitRejectedException>(e => e.RetryAfter > TimeSpan.Zero)
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: (i, e, ctx) =>
{
var rle = (RateLimitRejectedException)e;
return rle.RetryAfter;
},
onRetryAsync: (e, ts, i, ctx) => Task.CompletedTask
);
_wrappedPolicies = Policy.WrapAsync(_retryPolicy, _rateLimitPolicy);
Currently, once the retry limit of 3 is exceeded it throws RateLimitRejectedException. I want to throw a custom error if the retry limit is exceeded. Does anyone know how to do it?
If you want to throw an exception then use System.Exception with an if statement
So:
if(retryLimit>3)
{
throw new System.Exception("Retry Limit exceeded 3.");
}
If you don't wish to interrupt the process with a thrown exception, you can use a try{}catch(Exception e){} too
Whenever you want to execute your _wrappedPolicies
then you can call either ExecuteAsync or ExecuteAndCaptureAsync methods.
Former throws the original exception in case of retry
Latter captures the result in a PolicyResult both in failure and success cases
PolicyResult policyExecutionResult = await _wrappedPolicies.ExecuteAndCaptureAsync(...);
On this result object there is a property called FinalException. You can examine that and based on the assessment result you can throw a custom exception
if (policyExecutionResult.FinalExecption is RateLimitRejectedException)
throw new CustomException(...);

Logs not written within C# Async method that thrown an exception

I've been assigned a bug to investigate. It is occurring within a component in one of our integration test environments. The exception thrown and logged is fairly generic, and completely unhelpful, so I set about augmenting the module with appropriate logging to help track down the source of the problem.
Once the logging code was shipped up, the test case ran, and failed as expected, and I eagerly summoned the logs. To my surprise there were none present past a particular async method call, despite the fact that I had narrowed the problem down to somewhere within this method.
public async Task<IResult<XmlDocument>> Handle(TrustedThirdPartyRequest request)
{
// do stuff
// do more stuff
// Exception thrown after here
Logger.Info(m => m("Someflag :{0}", someFlag)); // logs successfully
try
{
if (someflagIsOn && await deriveThisThing(clientId)) //<- Exception thrown in deriveThisThing
{
Logger.Info(m => m("Am I called?")); // no! I am not!
}
}
catch (Exception e) // super unhelpful base exception handler
{
Logger.Info(m => m("Unhelpful exception log"));
}
}
And the method in some other class
private async Task<Thing> deriveThisThing(int clientId)]
{
Logger.Info(m => m("Entered deriveThisThing")); // <-- Line is not logged
var somethingInteresting = await ThingThatThrowsException(clientId); // <-- thing throws exception
Logger.Info(m => m("Entered deriveThisThing")); // <-- Line is not logged
return Thing.Whatever;
}
This is an API project and we're using log4net. Now, I've probably omitted a mass of further information, like logger config, etc, that is needed to answer this, but at the moment I'm hoping that there's an obvious answer to this without all of that. Any clues, folks?

Set durationOfBreak in Polly CircuitBreaker

I am using Polly Circuit Breaker for an API which throws a user defined Exception if service is down for maintenance.
The Exception will contain a value how long the service will be down.
Is it possible to configure/update the circuit breaker policy after first user exception is handled ?
e.g.
CircuitBreakerPolicy breaker = Policy.Handle<UserException>()
.CircuitBreaker(
exceptionsAllowedBeforeBreaking: 1,
durationOfBreak: TimeSpan.FromMinutes(1));
Example()
{
try
{
string response = await breaker.ExecuteAsync<String>(() =>
{
return client.GetStringAsync("/api/values/");
});
}
catch (UserException ex)
{
var downtime = GetDowntime(ex);
//how to update the duration of break ?
breaker.durationOfBreak = downtime;
}
catch (Exception ex)
{
…
}
}
It is not possible to change the durationOfBreak on an existing CircuitBreakerPolicy instance, after creation.
For general dynamic reconfiguration during running, the Polly team recommends atomically updating a policy instance stored in the PolicyRegistry.
For the specific scenario:
[the] API throws a user defined Exception if service is down for maintenance [which] will contain a value how long the service will be down
you could, more simply, use the circuit-breaker's manual controls:
.Isolate() to manually force the circuit open;
.Reset() to manually reset the circuit.
You could .Isolate() the circuit when the given exception is thrown; and set a Timer to call Reset() on it at the time that the exception indicates the service should be available again.

Polly WaitAndRetry with final exception does nothing

I execute calls to an external service that is not very stable, and thus throws WebExceptions.
I want to retry a few times and after the last attempt I want to throw the last error received.
This is my attempt with Polly (v6.1.1):
public static Policy WaitAndRetryPolicy<T>(short nrOfRetryAttempts = 5) where T : Exception
{
var waitAndRetry = Policy
.Handle<T>()
.WaitAndRetry(nrOfRetryAttempts, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
var fallbackForLastError = Policy
.Handle<T>()
.Fallback(
fallbackAction: () => { },
onFallback: (ex) => { throw ex; });
return Policy.Wrap(fallbackForLastError, waitAndRetry);
}
Caller, legacy VB.Net:
Dim retryPolicy = Policies.WaitAndRetryPolicy(Of WebException)()
Dim theResult = retryPolicy.
ExecuteAndCapture(Function()
Return aProxy.GetSomething(a, b)
End Function).Result
When I run the code as depicted above, theResult stays null and it seems like the service is not called.
If I just use the WaitAndRetryPolicy without the Fallback function, the service is called and the retry mechanism works as expected (without throwing the exception of course).
How can I achieve my goal, without having to check PolicyResult.FinalException in the caller code?
To have Polly rethrow any final exception, rather than capture it into PolicyResult.FinalException, simply execute the policy with the .Execute(...) or .ExecuteAsync(...) overloads, rather than .ExecuteAndCapture(...) or .ExecuteAndCaptureAsync(...)
I don´t know about the last exception but i have implemented a very similar behaviour with Retry and CircuitBreakerException(with Wrapping). So you can try 3 times and throw a circuitbreakerexception after 2 failures. Then you are able to react on the last exception.
Policy
.Handle<SomeExceptionType>()
.CircuitBreaker(2, TimeSpan.FromMinutes(x));

Categories

Resources