GuilhermeJacob
GuilhermeJacob

Reputation: 19

How to speed up Api Controller Response using asynchronous code to run another task when a request is received?

In my api controller, I have a method called "SendMail" that receives a post with a json body that looks like this:

{
    "MailTo": "[email protected]",
    "Subject": "This is the subject",
    "Body": "This is the body"
}

When a request is received, the method "SendMail" validates the data and if it is correct, the mail is sent.

[HttpPost]
public IHttpActionResult SendMail([FromBody] MailData mailData)
    {
        if(ValidateMailData(mailData) == true)
        {
             Email.send(mailData);
             return Ok("Your email was sent!");
        }

        return BadRequest("Some error");
    }

The problem is that the caller will only receive a response from the api when the mail is sent (and is taking to long), but I would like to only validate the data, return the response to the caller and then send the mail asynchronously (it's not important to the caller if the mail was actually sent or not, and if the data is validated correctly, it will be sent). This code is just an example, but illustrates precisely the problem that I am facing.

Upvotes: 0

Views: 539

Answers (1)

Enrico Massone
Enrico Massone

Reputation: 7348

You should decouple the web layer of your application from the actual service layer.
This is usually done by using queues.

The idea is that you have a web layer whose responsibility is getting user requests for some kind of work to be done, in your case the actual work is sending an email. The web service implementing the web layer of your app should expose an endpoint that a user can call in order to request that an email is sent. The web service endpoint should validate the content of the user request based on business rules that depends on your requirements.

If the user request is a valid one you will then enqueue the request in a dedicated queue and, then, you return a 202 accepted status code response (notice that you don't have to actually send the email before replying to the HTTP request, you just have to be sure that the user request has been successfully enqueued).

If, otherwise, the user request is not valid based on your validation rules you will return a 422 unprocessable entity status code, so that the user understands that its request is not valid.

The enqueued requests are, in the meantime, processed by one or more worker services which are separate applications (they are completely decoupled from the web server) whose responsibility is implementing the actual work to send the emails.

You can decide to use different technologies for the web server and the backend services, they just have to agree on the format of the messages enqueued by the web server and dequeued by the backend services. There are many ways to do so.

Notice that this kind of architecture is a bit more complicated than having just the web service, but it has many advantages. There is a clear separation of the concern and each piece of the infrastructure is able to scale well because it does only one thing and there isn't any shared resource contended by the different parts of your system (well, the queue is actually contended but there are very good technologies to help you implementing a queue the right way, like a service bus for example).

Another important thing to notice is that using async code in the web service layer is not a way to replying quicker to the single user, but instead is a technique useful to better scale the web server itself. Threads are important resources and are used by a web server to reply to incoming HTTP requests. When you perform IO tasks you should never block a thread letting it idle waiting for the IO task to complete. You should, instead, use an async api so that your thread starts the async task (for instance enqueuing a new request to send an email) and it is immediately returned to the thread pool, so that it can be used to reply to another incoming HTTP request (or doing other useful stuff) while the IO operation is being performed in the meantime by the operating system.

Think to this analogy (this was originally written by Jon Skeet). Imagine that you are at home and you want to eat a pizza. You decide to call your favourite restaurant and to order a pizza (in this analogy you are the thread and getting a pizza at home is the IO task).

Do you think is more efficient to order the pizza and then do other useful things while the pizza is traveling from the restaurant to your home, or sitting in front of the door waiting for the pizza and doing nothing in the meantime ? Well, if you are a web server you are very busy and for sure you have a lot of important things to do (read service incoming HTTP requests) while the pizza is traveling to your home.

Upvotes: 2

Related Questions