Asynchronous Processing with message queues
Tags: systemdesign, queue, asynchronous
Published: 2019-09-24 13:21:45

---
title: Asynchronous Processing with message queues
published: true
description: 
tags: system-design, queue, asynchronous
cover_image: https://www.translatorscafe.com/static/ucvt/img/Binary_Tunnel_1.jpg
---

Ever heard of *fire and forget* processing? If not, a short and simple scenario which can help us understand this is where a *caller* returns immediately upon invocation, while the actual execution of *callee* is being executed parallelly. In a nutshell, *caller* is not blocked for the task to be finished after initiating *callee's* execution. Let's see a real-life example to understand it better.

```kotlin
class RequestController {
  @PostMapping("/order")
  fun placeOrder(): Response {
    OrderService().placeOrder()
    MailService().sendMail()
    return Response.SUCCESS
  }  
}
```
```kotlin
class MailService {
  fun sendMail() {
    Thread {
      // mail sending logic
    }.start()
  }
}
```
In above *kotlin* snippet, we can see RequestController upon receiving the request does two things:
1. Place an actual order by using `OrderService`.
2. Send a mail notification using `MailService`.

If we again focus on `MailService` code, it sends a mail in a new `Thread`. By using a new `Thread`, `MailService` is ensuring that it's caller isn't blocked for it to finish. Since sending a mail could be a heavy IO operation (establishing a connection with SMTP server which is prone to network errors), Hence `Client` shouldn't wait for the server to send mail which can be executed in an `Asynchronous` manner.

Apparently, if an order is placed but mail sending logic fails because of any error. `Client` would still receive a **success** response indicating the status of an order placed. Since mail notification is a way to notify the user regarding the order placed, it's failure shouldn't be a snag in the user's main journey.

It's kind of **Notification** to `MailService` to initiate its execution which is achieved by calling a function of `MailService`. This kind of communication is required where time-consuming processing needs to be done, such as processing large volumes of data but no immediate result is sought by the caller.

Now we all know what asynchronous procedures are and how they can be effectively leveraged in an application process, but *What about multiple processes? such as Microservices !!*

In a microservices architecture, each service is designed as a self-sufficient, independent piece of software. In such an architecture, a single-use case often consists of multiple HTTP calls to multiple services. HTTP calls are the synchronous way of microservices to communicate with each other because requester expects an immediate response. Similarly, we want our services to work in an asynchronous manner too. But wait, we can't call a function which is running as part of a different process (might be on a different machine). This is where [message-queue](https://en.wikipedia.org/wiki/Message_queue) comes to rescue us.

#### Message Queues
Message queues provide an asynchronous way of communication to multiple application processes. A process that passes the message to other process is called a *Producer* and the system receives the message is known as *Consumer*. *Producer* puts a message on the message-queue and does not require an immediate response and continues its own processing.

Our last `Email` example is a perfect candidate to understand this asynchronous messaging system.

![Message broker email example](https://thepracticaldev.s3.amazonaws.com/i/2lgq2gcwy39d4pcw8m2s.jpg)

When order service receives an order request from the client, it notifies email service by publishing a message on message broker and doesn't wait for it to finish and continue doing its own processing if any.

##### Decoupling
This way of communication decouples the *Producer* from *Consumer* i.e. they have no knowledge of each other and communicate through a message broker. Components in such an architecture are oblivious to each other and are easier to maintain, extend and debug.

![Message broker multiple consumers](https://thepracticaldev.s3.amazonaws.com/i/zntza4umk2bmaxru6t5f.jpg)

We just added two more components which are interested in listening `order placed` message. `SMS service` now can send a message when an order is placed and same way `Inventory service` can start packaging and deliver the goods. We can see how easy it is to extend the system to multiple consumers listening to the same kind of messages without **disturbing** the producer since `Message broker` is the only part which consumers are concerned about. These decoupled components can evolve differently with different languages, different frameworks or by different teams.

##### Reliability
Both *Producer(s)* and *Consumer(s)* need not interact with message breaker simultaneously. *Producer* and *Consumer* are totally isolated and need not follow each other states. *Producer* can just put the message on the queue and then continue processing. *Consumer* which is working independently will pick up the message from the queue when it is able to process them. If *Consumer* isn't ready or not available to process yet, those messages will be stored and delivered when *Consumer* is healthy and available to process the messages.

Scenarios, where you can't afford to miss these messages, can be a strong use case for Message queues. Ex: [GDPR compliance](https://gdpr.eu/). Let's say in a microservices architecture, Our application has the user data saved across multiple DBs. When a user from the provided user interface triggers a [Delete my data](https://gdpr-info.eu/art-17-gdpr/) action, that action has to be propagated to multiple services to delete data from multiple DBs. What if one of the services is not healthy and not able to receive any HTTP action, It'll not be triggered and data remain undeleted which makes the whole system GDPR *uncompliant*. 😵

Reliable delivery of messages would be beneficial in this scenario. On the initial trigger, a *delete all data* can be published to all the queues. At the appropriate time, when consumers are healthy and ready to process messages, they will be delivered to and processed by consumers.


#### Conclusion
We saw how async processing helps in building interactive systems by reducing the waiting time for end-users. Along with that, we also saw how message queues bring in decoupling resulting in a well structured, extendable, maintainable and easy to debug systems.
Last but not least, *Reliability* ensures *No message left behind. :)*