Right method to Use Polly C# Library to handle Exception - c#

I am trying to use Polly to handle exceptions thrown by my WebRequest.
This is my implementation.
var generalExceptionPolicy=Policy.Handle<Exception>().WaitAndRetry(2, retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),(exception,timespan)=>{
if(attempt++ == 2)
{
Toast.MakeText(Activity,"No Connection Bro",ToastLength.Short).Show();
}
});
var test = await generalExceptionPolicy.ExecuteAsync(()=>PostPreLogin (view.FindViewById<EditText> (Resource.Id.mobileTextBox).Text));
I have got the retries working. But what I am wondering is where will I get a callback after the last attempt ? I am getting a callback in Policy definition part, where I am trying to display a Toastmessage. But that is only between the trials. I am not getting it after my last trial.
Also, my UI freezes after the last trial. Maybe becuase ExecuteAsync Task did not complete, due to the Exception. If that is so, what is the right approach to use Polly library ?
This is the method that I am trying to handle with Polly
public async Task<string> PostPreLogin(string userName)
{
var preloginvalue = await Account.PreLoginPost (userName);
return preloginvalue;
}

It is easy, basically you need to define the policy and passing the call back later is just as simple . Check this article which describes how to accomplish exactly what you want in details.
Basically, you can define your policy as follows :
async Task<HttpResponseMessage> QueryCurrencyServiceWithRetryPolicy(Func<Task<HttpResponseMessage>> action)
{
int numberOfTimesToRetry = 7;
int retryMultiple = 2;
//Handle HttpRequestException when it occures
var response = await Policy.Handle<HttpRequestException>(ex =>
{
Debug.WriteLine("Request failed due to connectivity issues.");
return true;
})
//wait for a given number of seconds which increases after each retry
.WaitAndRetryAsync(numberOfTimesToRetry, retryCount => TimeSpan.FromSeconds(retryCount * retryMultiple))
//After the retry, Execute the appropriate set of instructions
.ExecuteAsync(async () => await action());
//Return the response message gotten from the http client call.
return response;
}
And you can pass the callback as follows:
var response = await QueryCurrencyServiceWithRetryPolicy(() => _httpClient.GetAsync(ALL_CURRENCIES_URL));

I think what you are after is ExecuteAndCapture instead of Execute:
generalExceptionPolicy.ExecuteAndCapture(() => DoSomething());
Check this out for more details.

Related

I have to handle an exception in Polly where I need to Compare the data in the result set with the data in Cosmos Database and then Retry

For every 200 status code that we're receiving from the gremlin query, I want to check if the result vertex matches with the vertex that needed to be updated. And if it doesn't match then do a retry.
public async Task<string> ExecuteUpdateQueryAsync(string query, bool ignoreConflict = true)
{
try
{
var repsonse = await Policy
.Handle<ResponseException>(x => (long)x.StatusAttributes["x-ms-status-code"] != (long)HttpStatusCode.Conflict)
.OrResult<ResultSet<dynamic>>(r => r.StatusAttributes["x-ms-status-code"] == 200)
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))
.ExecuteAsync(() => gremlinClient.SubmitAsync<dynamic>(query));
logger.LogDebug(query + JsonConvert.SerializeObject(repsonse.StatusAttributes));
if (repsonse.Count > 0)
{
return JsonConvert.SerializeObject(repsonse);
}
return default;
}
...
}
EDIT:
The response we get from the Gremlin client looks something like : (hope this helps)
g.V(['1111|2222|3333','1111|2222|3333']).Property('Transaction_Quantity',4500).Property('Transaction_Type','Repack'){"x-ms-status-code":200,"x-ms-activity-id":"a4af8faf-4aa9-4ae2-8dd8-797bdbd80a97","x-ms-request-charge":34.64,"x-ms-total-request-charge":34.64,"x-ms-server-time-ms":7.6626,"x-ms-total-server-time-ms":7.6626}
I want to be able to compare the transaction quantity in this response with the one that we are trying to update.
Disclaimer: I'm not familiar with CosmosDb's Gremlin API, so my suggestions regarding the API usage might be incorrect. I try to focus on the Polly side in my post.
So, according to my understanding you want to decide whether or not you need to perform further retry attempts based on the response. If one of the fields of the response matches with some value then you shouldn't otherwise you should.
In case of Polly the Handle, HandleResult, Or and OrResult policy builder functions are anticipating synchronous predicates. In other words they are designed to perform simple assertions (like: status code check, existence check, inner exception type check, etc...) Because of this, most of the time they are defined with simple lambda expressions.
If you need to perform more complex / asynchronous logic then you should put that logic next to your to-be-decorated method call. In other words that logic should belong to the to-be-retried function.
As I said inside the disclaimer I'm not familiar with the Gremlin API. According to this SO thread you can deserialize the response into a DTO by first serializing the ResultSet<dynamic> then deserializing that into YourDto.
private async Task<ResultSet<dynamic>> IssueGremlinQueryAsync(string query)
{
ResultSet<dynamic> results = gremlinClient.SubmitAsync<dynamic>(query);
var parsedResult = JsonConvert.DeserializeObject<YourDto>(JsonConvert.SerializeObject(results));
//TODO: perform assertion against the parsedResult
return results;
}
Here you have several options how you propagate back the need for further retries to the policy:
Use a ValueTuple where one of the items is a flag
Use a custom exception
etc.
ValueTuple
private async Task<(bool, ResultSet<dynamic>)> IssueGremlinQueryAsync(string query)
{
ResultSet<dynamic> results = gremlinClient.SubmitAsync<dynamic>(query);
var parsedResult = JsonConvert.DeserializeObject<YourDto>(JsonConvert.SerializeObject(results));
//Replace this with your own assertion
bool shouldRetry = parsedResult.TransactionQuantity % 2 == 0;
return (shouldRetry, results);
}
The first item in the tuple is the flag, whereas the second is the unparsed results.
With this method you can refactor the related part of your ExecuteUpdateQueryAsync method like this:
public async Task<string> ExecuteUpdateQueryAsync(string query, bool ignoreConflict = true)
{
//...
var retryPolicy = Policy
.Handle<ResponseException>(x => (long)x.StatusAttributes["x-ms-status-code"] != (long)HttpStatusCode.Conflict)
.OrResult<(bool ShouldRetry, ResultSet<dynamic> Response)>(
x => x.ShouldRetry || (long)x.Response.StatusAttributes["x-ms-status-code"] == 200)
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
var response = await retryPolicy.ExecuteAsync(async () => await IssueGremlinQueryAsync(query));
//...
}
Exception
private async Task<ResultSet<dynamic>> IssueGremlinQueryAsync(string query)
{
ResultSet<dynamic> results = gremlinClient.SubmitAsync<dynamic>(query);
var parsedResult = JsonConvert.DeserializeObject<YourDto>(JsonConvert.SerializeObject(results));
//Replace this with your own assertion
bool shouldRetry = parsedResult.TransactionQuantity % 2 == 0;
return !shouldRetry ? results : throw new OperationFailedRetryNeededException("...");
}
If we should not retry then we return with the unparsed results otherwise we throw a custom exception.
With this method you can refactor the related part of your ExecuteUpdateQueryAsync method like this:
public async Task<string> ExecuteUpdateQueryAsync(string query, bool ignoreConflict = true)
{
//...
var retryPolicy = Policy
.Handle<ResponseException>(x => (long)x.StatusAttributes["x-ms-status-code"] != (long)HttpStatusCode.Conflict)
.Or<OperationFailedRetryNeededException>()
.OrResult<ResultSet<dynamic>>( x => (long)x.StatusAttributes["x-ms-status-code"] == 200)
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
var response = await retryPolicy.ExecuteAsync(async () => await IssueGremlinQueryAsync(query));
//...
}

Retry Failed Tasks in C#.Net -WhenAll Async [duplicate]

I have a solution that creates multiple I/O based tasks and I'm using Task.WhenAny() to manage these tasks. But often many of the tasks will fail due to network issue or request throttling etc.
I can't seem to find a solution that enables me to successfully retry failed tasks when using a Task.WhenAny() approach.
Here is what I'm doing:
var tasks = new List<Task<MyType>>();
foreach(var item in someCollection)
{
task.Add(GetSomethingAsync());
}
while (tasks.Count > 0)
{
var child = await Task.WhenAny(tasks);
tasks.Remove(child);
???
}
So the above structure works for completing tasks, but I haven't found a way to handle and retry failing tasks. The await Task.WhenAny throws an AggregateException rather than allowing me to inspect a task status. When In the exception handler I no longer have any way to retry the failed task.
I believe it would be easier to retry within the tasks, and then replace the Task.WhenAny-in-a-loop antipattern with Task.WhenAll
E.g., using Polly:
var tasks = new List<Task<MyType>>();
var policy = ...; // See Polly documentation
foreach(var item in someCollection)
tasks.Add(policy.ExecuteAsync(() => GetSomethingAsync()));
await Task.WhenAll(tasks);
or, more succinctly:
var policy = ...; // See Polly documentation
var tasks = someCollection.Select(item => policy.ExecuteAsync(() => GetSomethingAsync()));
await Task.WhenAll(tasks);
If you don't want to use the Polly library for some reason, you could use the Retry method bellow. It accepts a task factory, and keeps creating and then awaiting a task until it completes successfully, or the maxAttempts have been reached:
public static async Task<TResult> Retry<TResult>(Func<Task<TResult>> taskFactory,
int maxAttempts)
{
int failedAttempts = 0;
while (true)
{
try
{
var task = taskFactory();
return await task.ConfigureAwait(false);
}
catch
{
failedAttempts++;
if (failedAttempts >= maxAttempts) throw;
}
}
}
You could then use this method to download (for example) some web pages.
string[] urls =
{
"https://stackoverflow.com",
"https://superuser.com",
//"https://no-such.url",
};
var httpClient = new HttpClient();
var tasks = urls.Select(url => Retry(async () =>
{
return (Url: url, Html: await httpClient.GetStringAsync(url));
}, maxAttempts: 5));
var results = await Task.WhenAll(tasks);
foreach (var result in results)
{
Console.WriteLine($"Url: {result.Url}, {result.Html.Length:#,0} chars");
}
Output:
Url: https://stackoverflow.com, 112,276 chars
Url: https://superuser.com, 122,784 chars
If you uncomment the third url then instead of these results an HttpRequestException will be thrown, after five failed attempts.
The Task.WhenAll method will wait for the completion of all tasks before propagating the error. In case it is preferable to report the error as soon as possible, you can find solutions in this question.

How to cancel .Net Core Web API request using Angular?

I have the following two applications
Angular 6/7 App
.Net Core Web API
I am making GET request to API using Angular's HttpClient as shown below
this.subscription = this.httpClient.get('api/Controller/LongRunningProcess')
.subscribe((response) =>
{
// Handling response
});
API controller's LongRunningProcess method has the following code
[HttpGet]
[Route("LongRunningProcess")]
public async Task<IActionResult> LongRunningProcess(CancellationToken cancellationToken)
{
try
{
// Dummy long operation
await Task.Factory.StartNew(() =>
{
for (int i = 0; i < 10; i++)
{
// Option 1 (Not working)
if (cancellationToken.IsCancellationRequested)
break;
// Option 2 (Not working)
cancellationToken.ThrowIfCancellationRequested();
Thread.Sleep(6000);
}
}, cancellationToken);
}
catch (OperationCanceledException e)
{
Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {e.Message}");
}
return Ok();
}
Now I want to cancel this long-running process so I am unsubscribing from client side as shown below
// On cancel button's click
this.subscription.unsubscribe();
Above code will cancel the request and I can see it is canceled in the Network tab of the browser as shown below
But it is not going to make IsCancellationRequested to true in the method LongRunningProcess of the API, so the operation will keep going.
[Note]: Both Option 1 and Option 2 in API method are not working even if I make a call using postman.
Question: Is there any way to cancel that LongRunningProcess method's operation?
When angular cancel request, you can get cancellation token from http context
CancellationToken cancellationToken = HttpContext.RequestAborted;
if (cancellationToken.IsCancellationRequested)
{
// The client has aborted the request
}
You dont need break in this case only use like this
[HttpGet]
[Route("LongRunningProcess")]
public async Task<IActionResult> LongRunningProcess(CancellationToken cancellationToken)
{
for (int i = 0; i < 10; i++)
{
cancellationToken.ThrowIfCancellationRequested();
// Dummy long operation
await Task.Factory.StartNew(() => Thread.Sleep(60000));
}
return Ok();
}
You can read it more here
This is because your dummy long operation does not monitor the canncellationToken. I'm not sure it is actually your intention to start 10 one-minute tasks all in parallel without any delay, which is what your code does.
In order to have a dummy long operation, the code would be like
[HttpGet]
[Route("LongRunningProcess")]
public async Task<IActionResult> LongRunningProcess(CancellationToken cancellationToken)
{
// Dummy long operation
await Task.Run(() =>
{
for (var i = 0; i < 60; i++)
{
if (cancel.IsCancellationRequested)
break;
Task.Delay(1000).Wait();
}
});
return Ok();
}
Task.Run is just equivalent to Task.Factory.StartNew, by the way.
However, if you just need a dummy long-run operation in your web API, then you can also simply use Task.Delay, which supports cancellation token. Task.Delay throws an exception when the request is canceled, so add exception handling code when you need to do something after request cancellation.
[HttpGet]
[Route("LongRunningProcess")]
public async Task<IActionResult> LongRunningProcess(CancellationToken cancellationToken)
{
// Dummy long operation
await Task.Delay(60000, cancel);
return Ok();
}
Any http observables still running at the time will complete and run their logic unless you unsubscribe in onDestroy(). Whether the consequences are trivial or not will depend upon what you do in the subscribe handler. If you try to update something that doesn't exist anymore you may get an error.
Tip: The Subscription contains a closed boolean property that may be useful in advanced cases. For HTTP this will be set when it completes. In Angular it might be useful in some situations to set a _isDestroyed property in ngDestroy which can be checked by your subscribe handler.
Tip 2: If handling multiple subscriptions you can create an ad-hoc new Subscription() object and add(...) any other subscriptions to it - so when you unsubscribe from the main one it will unsubscribe all the added subscriptions too.
So, best practice is to use takeUntil() and unsubscribe from http calls when the component is destroyed.
import { takeUntil } from 'rxjs/operators';
.....
ngOnDestroy(): void {
this.destroy$.next(); // trigger the unsubscribe
this.destroy$.complete(); // finalize & clean up the subject stream
}
var cancellationToken = new CanellationToken();
cancellationToken.CancelAfter(2000);
using (var response = await _httpClient.GetAsync("emp",
HttpCompletionOption.ResponseHeadersRead, cancellationTokenSource.Token))
{
response.EnsureSuccessStatusCode();
var stream = await response.Content.ReadAsStreamAsync();
var emp = await JsonSerializer.DeserializeAsync<List<empDto>>(stream, _options);
}
Further we can also have this "CancellationToken" class, which is nothing much Http client method which terminates the request after certain time-interval.
In angular subscription.unsubscribe(); closes the channel and causes CORE to cancel the API caller's thread, that's good.
Don't use await Task.Run(()... This creates a result/task that should be disposed, if not, the task keeps going, your pattern doesn't permit this - that's why it continues to run.
Simply - 'await this.YourLongRunningFunction()', I'm pretty sure that when the owning thread throws the OperationCancelled exception your task will end.
If "3" doesn't work, then pass a cancellation token to your long running task and set that when you catch your OperationCancelled exception.

How do I use Polly for retries and transient fault handling of arbitrary "failure" conditions

I want to use Polly not to check for overt "failures" but rather for other conditions. Specifically, I want to make a single (async) call, for example httpClient.GetAsync(...) which for the purposes of this question I know will succeed - that is, after the execution of:
var response = await _myHttpRequestPolicy.ExecuteAsync(() => httpClient.GetAsync(uri));
response.IsSuccessStatusCode will be true.
Let's assume then I do the standard:
var content = await response.Content.ReadAsStringAsync();
and
content == { "Name":"Tom", "Age", 30", "ErrorCode":"12345" }
I want my policy logic to execute based on the contents (or absence or presence of) the ErrorCode portion of the response. So it's just a single call I'm making.
How can I do this with Polly?
You're configuring a policy to guard the HttpClient GetAsync method, which returns Task<HttpResponseMessage>. You want to configure a Policy<HttpResponseMessage> to work with this method, using async handlers.
Policy<T>.HandleResult(Func<T, bool> filter) allows you to look at the HttpResponseMessage and determine whether you want to handle that result.
A couple of options. One, you could figure out deserializing/reading the HttpResponseMessage's json payload within the HandleResult method. You only get a Func<HttpResponseMessage, bool> to work with. This would need to happen synchronously, as adding async/await changes the return type to Task.
Second, you could apply the policy at a higher level. Get the response as you are httpclient.GetAsync(uri), then deserialize the content. Maybe have one Policy<HttpResponseMessage> wrap the httpclient call, and one Policy<MyAbstractApiResponse> to look for the custom error code after deserializing?
As a note, an API error should really be picked up by the IsSuccessStatusCode property on the HttpResponseMessage. Your REST api (is it yours? that's an assumption) should be setting status codes appropriate to the error, not solely 200's and custom response properties.
Related further reading: Check string content of response before retrying with Polly
Update:
class Consumer
{
public void Test()
{
var apiResponse = Policy<IApiResponse>
.HandleResult(resp => !string.IsNullOrWhiteSpace(resp.ErrorCode))
// Setup Policy
.ExecuteAsync(() => otherClassInstance.MakeApiCall());
}
}
class OtherClass
{
HttpClient httpClient = ...;
public async Task<IApiResponse> MakeApiCall()
{
var response = Policy<HttpResponseMessage>
.HandleResult(message => !message.IsSuccessStatusCode)
// Setup Policy
.ExecuteAsync(() => httpClient.GetAsync(url));
var content = await response.ReadAsStringAsync();
return new ApiResponse(content);
}
}
I didn't look at real method names or syntax in putting that together so it might not compile. Hopefully you get the idea I'm trying to convey, one policy is called from within another.
As it was stated by mountain traveller the HandleResult was not designed to execute an async method in it. There are several workaround for this like:
using .GetAwaiter().GetResult() inside HandleResult
define two separate policies like it was in the linked github issue
or perform the async call inside the onRetryAsync and use a CancellationTokenSource to break the retry sequence
In this post let me focus on the last one
var noMoreRetrySignal = new CancellationTokenSource();
IAsyncPolicy<HttpResponseMessage> retryPolicy = Policy<HttpResponseMessage>
.HandleResult(r => r.StatusCode == System.Net.HttpStatusCode.OK)
.RetryAsync(3, onRetryAsync: async (dr, _, ctx) => {
var content = await dr.Result.Content.ReadAsStringAsync();
if (!content.Contains("ErrorCode")) //TODO: refine it based on your requirements
{
ctx["content"] = content;
noMoreRetrySignal.Cancel();
}
});
var result = await retryPolicy.ExecuteAndCaptureAsync(
async ct => await httpClient.GetAsync(address, ct),
noMoreRetrySignal.Token);
if(result.FinalException is OperationCanceledException)
{
Console.WriteLine(result.Context["content"]);
}
The noMoreRetrySignal is used to cancel the retry sequence
if there is no need for further retries
The retryPolicy checks whether the StatusCode is OK or not
The retryPolicy reads the response body asynchronously then performs some existence check
If it does not present then it stores the read string inside the Context and exits from the retry loop
If it presents then it continues the retry sequence
ExecuteAndCaptureAsync passes the noMoreRetrySignal's Token to the decorated method
The result of this call allows us to access the exception and the context
If the exception is caused by the CancellationTokenSource then we need to acces the context because the result.Result is null (that's why we had to use context to save the already read response body)

Xamarin Gcm Network Manager await httpclient

I'm using the Gcm Network Manager to schedule tasks, in one of those tasks I need to perform an HTTP request. Until now it was written with HttpWebRequest so nothing was async.
Now I would like to reuse code that is written with HttpClient and is async.
The problem that arrises is that I cannot make the OnRunTask() async as it needs to return an int:
e.g.
public override int OnRunTask(TaskParams #params)
{
var result = await performSync();
if(result)
{
return GcmNetworkManager.ResultSuccess;
}
return GcmNetworkManager.ResultReschedule;
}
What could I do to be able to reuse async code here ?
You can use Task.Run inside your OnRunTask method like this :
Task.Run( async () =>
{
// Do your stuff here
await asyncTask();
});
You will no need to have OnRunTask async with this technique
Hope it helps
Edit
If you need the return value to match the framework / library signature, you can also use .Result
E.g.
var result = asyncTask().Result;
...

Categories

Resources