7

Outbox Pattern: Reliably Save State & Publish Events

 3 years ago
source link: https://codeopinion.com/outbox-pattern-reliably-save-state-publish-events/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
Outbox Pattern: Reliably Save State & Publish EventsSkip to main content

What’s the Outbox Pattern? A reliable way of saving state to your database and publishing a message/event to a message broker.

Why do you need it? When you get into messaging, you will often find code that needs to save state to your database, and then publish a message/event to a message broker. Unfortunately, because they are two different resources, you need a distributed transaction to make these atomic.

There is another option to use the Outbox Pattern which does not require a distributed transaction and most messaging libraries support it.

YouTube

Check out my YouTube channel where I post all kinds of content that accompanies my posts including this video showing everything that is in this post.

Unreliable

To illustrate the issue, the first step is saving/changing the state in your primary database. It doesn’t matter if this is an RDMBS or a NoSQL Store.

step1-1024x302.png

Subsequently, since you have changed state, you need to now publish a message/event to a queue or message broker to let other systems know of the state change.

step2-1024x304.png

Since each step is independent, if there is a failure in publishing the event to the queue, then you’ve made a state change without letting other systems know of that change.

Outbox Pattern

Why is this a problem? If you’re using messaging in an event-driven architecture, you likely rely on events being published. An event not being published could have all sorts of implications.

For example, if you’re using events to invalidate a cache, you’ll now have stale data. Or worst, if you’re using events apart of a Saga (long-running process), the next portion/step of the saga will possibly never occur.

You need atomicity. All or nothing.

Outbox Pattern

The Outbox pattern solves this by using the transaction from your primary database to store your state changes along with the messages/events your publishing.

Outbox Pattern

This means that messages your publishing are initially stored in the same database alongside your other application data. Each messaging library may implement this slightly differently in terms of the structure of the data and messages as well as how their API looks, but this is the overall idea.

The order of saving state or publishing an event doesn’t really matter anymore as they are saved to the database in the same transaction.

Once we commit our transaction, a secondary process/thread (Publisher) will pull the unpublished events from the primary database.

Outbox Pattern

Then the Publisher will publish the events it pulled to the queue or message broker.

outbox3-1024x304.png

Finally, the Publisher will update/delete the records back to the database, so it knows that the events have been successfully published to the queue.

outbox4-1024x313.png

Code Example

In this example, I’m using CAP, but as mentioned, different messaging libraries will have different ways they implement this in their API.

CAP’s is really straight forward. It provides an extension method on the Entity Framework Core DatabaseFacade to begin a database transaction. You simply pass along the ICapPublisher when starting the transaction. This tells CAP to save the published event to the database, rather than directly to the message broker, in my case RabbitMQ.

using System; using System.Threading.Tasks; using DotNetCore.CAP; using Microsoft.AspNetCore.Mvc; using Sales.Contracts;

namespace Sales { public class PlaceOrderController : Controller { private readonly SalesDbContext _dbContext; private readonly ICapPublisher _publisher;

public PlaceOrderController(SalesDbContext dbContext, ICapPublisher publisher) { _dbContext = dbContext; _publisher = publisher; }

[HttpPost("/sales/orders/{orderId:Guid}")] public async Task<IActionResult> Action([FromRoute] Guid orderId) { using (var transaction = _dbContext.Database.BeginTransaction(_publisher)) { _dbContext.Orders.Add(new Order { OrderId = orderId }); await _dbContext.SaveChangesAsync();

var orderPlaced = new OrderPlaced { OrderId = orderId }; await _publisher.PublishAsync(nameof(OrderPlaced), orderPlaced);

await transaction.CommitAsync(); }

return NoContent(); } }

}

It’s really that straightforward. I’ve left out the configuration of CAP, which is also dead simple, it’s just a matter of using the correct data storage provider and specifying the configuration string in the ASP.NET Core Startup.ConfigureServices()

What this looks like in our primary database is a table that was created by CAP automatically called cap.Published. This is where it’s storing the published messages. Initially, the StatusName is null, and after successful publish to the queue, it updates it with Succeeded.

table-1024x350.png

At Least Once Messaging

You may have noticed in the original diagrams, that Publisher that pulls from the database, publishes to the queue, then marks the event published in the database, ultimately has the same problem we started out with.

Really we just moved the problem but have a different outcome. If we publish the message to the queue, but for some reason, CAP cannot update the StatusName of the record, then we will ultimately publish the same event again.

In a lot of situations, this is a better problem to have. We are now in an “At Least Once” scenario. Meaning, events will get published once or possibly more. In the original scenario, we were in an “At Most Once”, which also implies only once or possibly none.

Learn how to create Idempotent Consumers to handle duplicate messages safely.

Follow @CodeOpinion on Twitter

Enjoy this post? Subscribe!

Subscribe to our weekly Newsletter and stay tuned.

Leave this field empty if you're human:

Links


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK