I have a request execution:
var response = await client.ExecuteAsync(request, cancellationToken);
At some moment I want to cancel this operation, but the execution continues, so I have to check is cancellation requested on the next step of program workflow - it`s not good because this request will be completed although user doesnt need it anymore.
Related
In an Azure function event hub trigger (v3) it takes in a cancellation token in the Run method. When cancellation is signaled it means the server it shutting down. If I send this token to for example a Get operation using httpClient it will throw a TaskCanceledException and the function will end.
Will the events that this function was processing be sent to another instance of the function on another server or are they lost? Should cancellation be handle in a different way?
[FunctionName(nameof(MyFunction)), FixedDelayRetry(10, "00:00:15")]
public async Task RunAsync(
[EventHubTrigger("%InEventHubName%",
Connection = "InEventHubConnectionString",
ConsumerGroup = "%ConsumerGroup%")]
EventData[] events,
PartitionContext partitionContext,
CancellationToken cancellationToken)
{
foreach (var ev in events)
{
var response = await _httpClient.GetAsync("http://example.com/fetch?key=" + ev.Properties["Key"],
cancellationToken);
await Process(response, cancellationToken);
}
}
Will the events that this function was processing be sent to another instance of the function on another server or are they lost?
They are lost:
Unhandled exceptions may cause you to lose messages. Executions that result in an exception will continue to progress the pointer.
Should cancellation be handle in a different way?
You could choose to ignore cancellation. That may be best for this kind of situation.
(This discussion might not be specific to C#...)
I have a C# method SendMultipleRequests that sends HTTP POST request 10 times sequentially.
Is it possible for the server to receive requests out of order?
If my understanding is correct if the requests are sent concurrently (without await), the server could receive requests out of order, but in the example below it needs to wait for the response to be received at the client before sending next request, so the server will receive requests in order.
public async Task SendRequest(int i)
{
// definition of endpoint is omitted in this example
var content = new StringContent($"I am {i}-th request");
await HttpClient.PostAsync(endpoint, content);
}
public async Task SendMultipleRequests()
{
for (int i = 0; i < 10; i++)
{
await SendRequest(i);
}
}
with await your app will wait for the task returned by PostAsync to finish before it issues the next request - see the docs for postasync where it says “This operation will not block. The returned Task<TResult> object will complete after the whole response (including content) is read.” - using await will mean that you will only issue the next request after you I’ve read the content of the previous response
If you remove the await then your code will queue up ten tasks and start working on them all in some undefined order. The server will see requests in an unspecified order. This may be further exacerbated by the fact that the requests may take different routes through the internet, some slower. If you remove the await then you should capture the returned task into a list, then you can use something like await Task.WhenAll(list) to wait for them all to complete (unless you really want to fire and forget in which case you can assign them to the discard _ = client.PostAsync... but keeping the task allows you to discover and deal with exceptions that arose)
I am writing an azure function which synchronizes data every 15 minutes.
I am targeting .net 3.1 for azure function and I am using httpclient to send a get request to their API.
var response = await client.GetStringAsync()
It usually takes about 20 seconds to get the results, however, sometimes their server does not process the request correctly or it is just busy, and takes too long to do so or does not return any data. I've tested in postman, and I get status Ok 200, but it keeps loading for many minutes, and nothing happens.
However, if you just cancel the operation, and try again, it is responds fine.
Now My question is, how could I keep track of the time it takes for the result to come from my get client.GetStringAsync() method, and somehow cancel the operation if lets say it is longer than 2 minutes?
If you are using .NET 5, you can use a CancellationTokenSource with a 'cancel after' and pass the token to client.GetStringAsync(), like this:
var cts = new CancellationTokenSource()
cts.CancelAfter(120000); // Cancel after 2 minutes
try
{
var response = await client.GetStringAsync(uri, cts.Token);
}
catch (TaskCanceledException)
{
Console.WriteLine("\nTask cancelled due to timeout.\n");
}
Then you can wrap the operation in a try-catch block to see if the operation was cancelled by the token.
Reference: Cancel async tasks after a period of time (C#)
UPDATE
On .NET Core, you can specify a timeout for your HttpClient. Note however that this timeout applies to all requests you send using that instance of HttpClient. If you wish to do it that way, you can do as follows:
client.Timeout = TimeSpan.FromMinutes(2);
try
{
var response = await client.GetStringAsync(uri);
}
catch (TaskCanceledException)
{
Console.WriteLine("\nRequest cancelled due to timeout.\n");
}
var response = SaveOrderInDB();
OrderCreatedEvent orderCreatedEvent = new OrderCreatedEvent(x, y, z);
_requestRouter.Publish(orderCreatedEvent);
return response;
By MediatR docs the notifications is "Fire and forget" feature. I do not use await since I want to return immediately "response" object to client Angular app after notification was published.
However when I put breakpoint in notification handler I see in Chrom dev tools that request still in pending status, waits for notification to finish.
public Task Handle(OrderCreatedEvent notification, CancellationToken cancellationToken)
{
return Task.CompletedTask; // Breakpoint is here
}
How can I not wait for notifications to finish?
As mentioned in the comments, the breakpoint is preventing them from completing.
If you don't believe this, change your NotificationHandler to something like:
public async Task Handle(OrderCreatedEvent notification, CancellationToken cancellationToken)
{
await Task.Delay(5000);
Console.Write("Done."); //put a breakpoint here
}
Put a breakpoint on the Console.Write method, then run your application, and call your endpoint.
You'll see the response isn't pending, and after 5 seconds, your breakpoint is hit.
(or "Done." is written to the console if you didn't set a breakpoint)
MediatR's Notifications aren't "Fire and forget" by default. The docs state under the heading "Publish strategies".
The default implementation of Publish loops through the notification handlers and awaits each one. This ensures each handler is run after one another.
If you want to have notifications not be awaited on you will have to override the behaviour of the PublishCore method in the mediator.cs class.
The documentation mentions this can be done under the same header above and points to some sample code under MediatR.Examples.PublishStrategies. Have a look at the method ParallelNoWait in Publisher.cs in that sample to see how it works.
I have a web-api with mvc that's doing a bunch of startup initialization which will take a few minutes. I want the url to respond to a request during this time with a progress-indicator. My thinking was to use a middleware to accomplish this something like this:
public async Task Invoke(HttpContext httpContext)
{
await httpContext.Response.WriteAsync("Loading...");
await Task.Delay(5000); // the initialization-stuff (which is not started here but just waited on)
httpContext.Response.Clear();
await _next(httpContext); // continue to my MVC-page
}
However this does not seem to work (ERR_INCOMPLETE_CHUNKED_ENCODING). How do I properly clear/reset the respons so that I can write a new real response once the initialization is done.
I resorted to something like this instead (good enough):
public async Task Invoke(HttpContext httpContext)
{
if (!task.IsCompleted)
await httpContext.Response.WriteAsync("Loading...");
else
await _next(httpContext); // continue...
}
Once you have sent data to the client you can't take it back. You can't replace an existing page, only append.
You can therefore do two things:
You delay the response until initialization is complete (likely not feasible).
You send something else and end the request. You could make the page that you are sending poll the server using AJAX to see if initialization has been completed. Then, the page can reload itself.
Create a new API endpoint that replies with the initialization status. Make page page poll that endpoint every few seconds.