How to handle a reply for long-running REST request? - c#

Background: We have import-functions that can take anywhere from a few seconds to 1-2 hours to run depending on the file being imported. We want to expose a new way of triggering imports, via a REST request.
Ideally the REST service would be called, trigger the import and reply with a result when done. My question is: since it can take up to two hours to run, is it possible to reply or will the request timeout for the caller? Is there a better way for this kind of operation?

What I use in these cases is an asynchronous operation that returns no result (void function result in case of c# Web API), then send the result asynchronously using a message queue.
E.g.
[HttpPut]
[Route("update")]
public void Update()
{
var task = Task.Run(() => this.engine.Update());
task.ContinueWith(t => publish(t, "Update()"));
}

Related

Respond to MailGun's HTTP post and then process the message

When receiving mail through MailGun they require a response within a limited time. I have two issues with this:
1) After receiving the message I need to process and record it in my CRM which takes some time. This causes MailGun to time out before I get to send a response. Then MailGun resends the message again and again as it continues to time out.
2) MailGun's post is not async but the api calls to my CRM are async.
So I need to send MailGun a 200 response and then continue to process the message. And that process needs to be in async.
The below code shows what I want to have happen. I tried using tasks and couldn't get it working. There are times when many emails can come in a once (like when initializing someone's account) if the solution requires some sort of parallel tasks or threads it would need to handle many of them.
public class HomeController : Controller
{
[HttpPost]
[Route("mail1")]
public ActionResult Mail()
{
var emailObj = MailGun.Receive(Request);
return Content("ok");
_ = await CRM.SendToEmailApp(emailObj);
}
}
Thank you for the help!
The easiest way to do what you are describing (which is not recommended, because you may lose some results if your app crash) is to use a fire & forget task:
var emailObj = MailGun.Receive(Request);
Task.Run(async () => await CRM.SendToEmailApp(emailObj));
return Content("ok");
But, I think what you really want is sort of a Message Queue, by using a message queue you put the message in the queue (which is fast enough) and return immediately, at the same time a processor is processing the message queue and saves the result in the CRM.
This is what it'll look like when you use a message queueing broker.

WebApi how to send asyncronous email after returning response to user?

Emails are taking too long to send (sometimes up to 7-8) seconds.
I want to send a response back to my clients without them having to wait for the email to send. Is this possible?
public async Task<IHttpActionResult> Action()
{
//Do something
await email.sendAsync(); //Can take up to 10 seconds...
return Ok();
}
I can remove the await, but I obviously get a An asynchronous module or handler completed while an asynchronous operation was still pending
How can I achieve this?
Ideally you would want to offload this work onto some other process, maybe a queue and then have some subscribers process off that queue to send the email but that is a lot of work and we live in the real world.
You can use
HostingEnvironment.QueueBackgroundWorkItem(ct => email.sendAsync());
to get something quite reliable up and running.

async method in ASP.NET MVC

I want to create a async method which will open a thread and complete several tasks that is relatively independent to the current http request (for example, sending emails and generating files both of which takes time)
I created the following method
private static async Task<T> DoSeparateTask<T>(Func<T> func)
{
return func();
}
and want to use it in such a way:
private static void DoSomething()
{
#step 1 - some code for immediate processing
#step 2 - some code for generating documents which takes time
var additionalDocumentGenerationWork = DoSeparateTask<Document>(() =>{
#additional code for generating Document
#even though it returns Document, but 99% time it wont be used in this method
});
#step 3 - some code for sending SMTP email which takes time
var additionalDocumentGenerationWork = DoSeparateTask<bool>(() =>{
#additional code for sending email
return true;
});
}
Everything compiles, however when I run the web application, it still keeps loading and waiting everything to complete before rendering the web page. As the 2 additional tasks (email and documents) are not relevant for displaying the webpage, how can I achieve such by using the async modifiers?
Or if my understanding of async is wrong... please help me to correct...
Many thanks.
Or if my understanding of async is wrong... please help me to correct...
As I explain on my blog, async does not change the HTTP protocol. At the end of the day, you only have one response for each request. To put another way, async yields to the thread pool, not to the client.
What you're looking for is a way to do operations outside the request/response lifecycle, which is not directly supported by ASP.NET. I describe a few approaches on my blog. The best (most reliable) approach is to place the work into a queue, and have an independent backend that processes the queue (such as an Azure worker role).
You don't need the DoSeparateTask<T>() method. Instead, just use Task.Run<T>():
private static async Task DoSomething()
{
// #step 1 - some code for immediate processing
// #step 2 - some code for generating documents which takes time
var additionalDocumentGenerationWork1 = await Task.Run(() =>
{
// #additional code for generating Document
// #even though it returns Document, but 99% time it wont be used in this method
});
// #step 3 - some code for sending SMTP email which takes time
var additionalDocumentGenerationWork2 = await Task.Run(() =>
{
// #additional code for sending email
return true;
});
}
Note that the type of additionalDocumentGenerationWork1 and additionalDocumentGenerationWork2 (you used the same name twice in your example, so I changed it above to make the code legal) are Document and bool respectively. You can use additionalDocumentGenerationWork1 in your second task invocation if you like (e.g. to pass the result of the first operation).

How to test concurrency scenarios in .NET?

I've worked with concurrency, but I don't know any good way to test it.
I would want to know if there is any way to "force" Tasks to execute in an specific order to simulate tests cases.
For example:
Client #1 makes a request
Server starts retrieving data to Client #1
Client #2 makes another request while the server is still responding to Client #1
Assert << something >>
I have seen some people using custom TaskSchedulers. Does it make sense?
I had had that problem in several occurrences too. Eventually I have created a helper that can start a bunch of threads to execute concurrent actions. The helper provides synchronization primitives and logging mechanisms. Here is a code fragment from a unit test:
[Test]
public void TwoCodeBlocksInParallelTest()
{
// This static method runs the provided Action delegates in parallel using threads
CTestHelper.Run(
c =>
{
Thread.Sleep(1000); // Here should be the code to provide something
CTestHelper.AddSequenceStep("Provide"); // We record a sequence step for the expectations after the test
CTestHelper.SetEvent();
},
c =>
{
CTestHelper.WaitEvent(); // We wait until we can consume what is provided
CTestHelper.AddSequenceStep("Consume"); // We record a sequence step for the expectations after the test
},
TimeSpan.FromSeconds(10)); // This is a timeout parameter, if the threads are deadlocked or take too long, the threads are terminated and a timeout exception is thrown
// After Run() completes we can analyze if the recorded sequence steps are in the correct order
Expect(CTestHelper.GetSequence(), Is.EqualTo(new[] { "Provide", "Consume" }));
}
It can be used to test client/server or synchronization in components or simply run a thread with a timeout. I'll continue to improve this in the next weeks. Here is the project page:
Concurrency Testing Helper
This shouldn't be too difficult to simulate using tasks:
private async Task DoSomeAsyncOperation()
{
// This is just to simulate some work,
// replace this with a usefull call to the server
await Task.Delay(3000);
}
Now, lets consume it:
public async Task TestServerLoad()
{
var firstTaskCall = DoSomeAsyncOperation();
await Task.Delay(1000); // Lets assume it takes about a second to execute work agains't the server
var secondCall = DoSomeAsyncOperation();
await Task.WhenAll(firstTaskCall, secondCall); // Wait till both complete
}
This is basic producer-consumer problem in concurrency. If you want to test that case, just put a Thread.Sleep(100) to server which part responds consumers. In that way, your server will have a delay before sending response. And you can Invoke service request simply creating new threads in a loop.

Issue using Task.Factory.FromAsync

I enjoyed the new C# 5's async and await and I want to set it up to one of my previous Tcp application which I used the async whay (not async & await, in fact Begin*, End*)
in my application every message have a response, so every time you use BeginSend, you will receive a message related the message you first sent. (lets suppose command and it's report)
I want to add a new function called RequestAsync that you send your message and wait till the response comes. let me show the usage
string response = await RequestAsync("hi");
on this occasion, you will send a "hi" message and then code waits till the response related to this comes.
I had problems using Task.Factory.FromAsync, so I tried the following code, i want to make sure the performance is roughly the same as TaskFactory way.
public async Task<IRequestResult> SendRequestAsync(IRequest request, string message)
{
IRequestResult result = BeginSendRequest(request, message);
while (!result.IsCompleted)
{
await Task.Delay(1);
}
return result;
}
and BeginRequest is my own method.
Sincerely yours,
Peyman Mortazavi
No, that's going to be pretty awful in terms of efficiency - you'll be waking up and requiring rescheduling every 5 milliseconds. You've added latency while you effectively sleep between cycles as well.
You should go back to trying to use Task.Factory.FromAsync - you haven't told us what went wrong when you tried that, so it's hard to help you fix it... It's possible that you haven't implemented the asynchronous programming model pattern correctly to start with. (The fact that you're not providing a callback to your BeginSendRequest method is suggestive of that.)
I hope your real code doesn't have catch {} either...
Poling can never be efficient as IO completion ports, and why even use an async method if you are only waiting for it and not doing anything in the mean time, it just ads needles overhead.

Categories

Resources