Event Forwarding Pattern
In an event driven system there are different points at which message loss could happen resulting in data loss. The Event Forwarding pattern is one of the solutions to avoid this.
Like every other architectural pattern this has its own pros and cons.
While this pattern ensures data integrity in all scenarios, the overall performance and throughput of the system decreases by a third, according to some estimates.
Contents:
1. Challenge
2. Concept
3. Points of Failure
4. Code Implementation
Let's understand the problem and proposed solution.
Challenge:
Make sure that no messages are lost when passing data from one system to another.
Pattern used: Event Forwarding Pattern
Concept :
For illustrating the problem and pattern lets consider two Services A and B, along with a event broker and database.
Service A publishes events into Event Broker. Service B subscribes to the said events, processes them and then stores the result in a database.
Before applying any pattern visualise the usual flow.
![]() |
Illustration : A sample flow of a simple distributed system |
In the above example there are 3 places where events could be lost. Let's examine each of these one by one and solution for such scenarios.
Possible Point of Failure
Scenario 1:
Service A has published but message did not reach Event Broker, or broker did receive the message but was not able to persist it. Service A has no way of knowing if something went wrong.
![]() |
Illustration : Scenario 1 failure |
Solution :
Synchronous send to broker : Service A sends the event to broker and waits for an acknowledgement that the event has been received and persisted by the broker.
Incase the broker goes down, and ack is not received then there is a retry mechanism at Service A to resend the same message.
![]() |
Illustration : Scenario 1 Solution |
Scenario 2:
Service A has published the message successfully to broker, Service B has subscribed and read the message for processing.
But while processing something goes wrong and the service goes down. Now as far as the broker is concerned, the message is read successfully so it will mark it as read. But in reality the message is not processed successfully and there is no way to re process the message.
![]() |
Illustration : Scenario 2 failure |
Solution:
Client Acknowledge mode : Broker marks the message with a client id of service currently processing.
Incase of crash, broker erases the client id for some other service instance to pick up.
![]() |
Illustration : Scenario 2 solution |
Scenario 3:
Service B has sent the data for commit to DB but commit fails. As far as the broker is concerned the message is processed but in reality the message is not committed.
Service B cannot pick the message for reprocessing.
![]() |
Illustration : Scenario 3 failure |
Solution:
Last Participant Support : Post commit by service B on database, the next line of code sends ack to broker stating message is persisted.
the broker waits till it gets an ack that the message has been persisted before discarding the message.
![]() |
Illustration : Scenario 3 solution |
The above 3 problems and solutions cater to most of the scenarios where messages can be lost.
It is not mandatory to implement all the 3 stages, but decision needs to be taken on a case to case basis where some amount of data loss is acceptable while gaining on throughput of the system.
Code Implementation:
Traditionally if implemented using JMS ques or internal queues, all the above features need to be implemented individually.
Modern event brokers like kafka has all these features available out of the box and we just have to use it appropriately.
The sample implementation for above can be found here.
This implementation is kafka specific, with bonus implementation of dead letter queue too in the code
Comments
Post a Comment