Search code examples
transactionsrabbitmq

Integrating RabbitMQ with database transactions


Imagine the situation:

var txn = new DatabaseTransaction();

var entry = txn.Database.Load<Entry>(id);
entry.Token = "123";
txn.Database.Update(entry);

PublishRabbitMqMessage(new EntryUpdatedMessage { ID = entry.ID });

// A bit more of processing

txn.Commit();

Now a consumer of EntryUpdatedMessage can potentially get this message before the transaction txn is committed and therefore will not be able to see the update.

Now, I know that RabbitMQ does support transactions by itself, but we cannot really use them because we create a new IModel for each publish and having a per-thread model is really cumbersome in our scenario (ASP.NET web application).

I thought of having a list of messages due to be published when a DB transaction is committed, but that's a really smelly solution.

What is the correct way of handling this?


Solution

  • RabbitMQ encourages you to use publisher confirms rather than transactions. Transactions do not perform well.

    In any case, transactions don't usually work very well with a service oriented architecture. It's better to adopt an 'eventually consistent' approach, where failure can be retried at a later date and duplicate idempotent messages are ignored.

    In your example I would do the database update and commit it before publishing the message. When the publisher confirm returns, I would update a field in the database record to indicate that the message had been sent. You can then have a sweeper process come along later, check for unsent messages and send them on their way. If the message did get through, but for some reason the confirm, or subsequent database write failed, you will get a duplicate message. But that won't matter, because you've designed your messages to be idempotent.