HTTP request in pending status until MediatR notifications executed - c#

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.

Related

How to handle CancellationToken in Azure function eventhub trigger

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.

Async Await control flow in Web API

webcontroller {
async Task<ActionResult<string>> doSomething() {
var stringResult = await doSomethingAsync();
return stringResult;
}
}
what will be control flow here? will the controller return dummy response (ActionResult) to client after reaching doSomething() method call or the control remain in the web controller and return the stringResult to client? consider doSomething() is doing some network intensive tasks which might take more time to complete. Can anyone please explain the same to me if possible? Thanks in Advance!
will the controller return dummy response (ActionResult) to client
after reaching doSomething() method call or the control remain in the web controller and return the stringResult to client
It will not return anything to the client until doSomething method finished.
consider doSomething() is doing some network intensive tasks which
might take more time to complete
In this case you will have timeout on the client.
You have to start background job. Return to the client that task has been started. Then tell somehow to the client that task is finished.
Another source of information: Long running task in WebAPI
I recommend reading an article I wrote about how async works on ASP.NET.
will the controller return dummy response (ActionResult) to client after reaching doSomething() method call or the control remain in the web controller and return the stringResult to client?
When doSomethingAsync returns an incomplete task, then the await in doSomething will also return an incomplete task. Then the ASP.NET runtime (asynchronously) waits for that task to complete before sending the response.
await in ASP.NET yields to the thread pool; it does not yield to the client.

Is it possible for HTTP server to receive requests out of order even if they are sent sequentially?

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

Async progress in ASP .NET Core middleware?

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.

WebApi Controller - Send Mail Async

I have a WebApi Controller that one of the parts is sending emails to a set of users.
[HttpPost]
[Authorize]
[Route("{id}/Do")]
public async Task<HttpResponseMessage> Post(int id, Model model)
...
await _emailService.SendAsync(message);
...
Now the method that sends the emails (SendGrid)
public override async Task SendAsync(MailMessage message)
{
var client =
new SmtpClient(SendGridServerName, SendGridServerPort)
{
Port = SendGridServerPort,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false
};
var credentials = new NetworkCredential(SendGridUserName, SendGridPassword);
client.EnableSsl = true;
client.Credentials = credentials;
message.From = new MailAddress(FromAddress);
await client.SendMailAsync(message);
}
Everything works, but it's very slow. I was expecting it to be fast, but the await _emailService.SendAsync(message); does not appear to be async. It stops there for a while.
Any ideas?
Thanks
What async does is allowing your server to run other threads while your slow method is executed asynchronously. I.e., when you await for an async method, the server executes a different thread until the async method finishes execution, and then keep running the thread that called the async method. Async actions in a controller are treated exactly in the same way behind the scenes. So, the response from your async action to the browser will not happen until the async email sending has finished.
NOTE: for example, if there are connectivity problems to the email server, or the DNS resolution, you'll usually get a time out after 30 seconds, so your thread will be slept during 30 seconds, and only then will send the answer to the browser
If you want to return the response to your browser quickly, you need to implement a different idea, which is to start a new thread that sends the email, but don't wait for it to finish, so that your thread keeps running and inmediately returns the asnwer to the browser. That's known as fire and forget. To understand what I'm speaking about, please see this: Fire and forget approach. And then read this other carefully: Fire and Forget (Asynch) ASP.NET Method Call. Take into account that MVC itself is threaded and you need to have it into account when using the fire and forget approach.
Obvioulsy in fire and forget, the controller will not be able to detect errors during the email sending, beacause the new thread runs on its own while the main thread has already finished. So you have to implement something to at least log the possible error, and ideally let the user now what happened (for example which reports that he can see later on). Please, see this: ASP.NET Exception Handling in background threads

Categories