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?
Every RabbitMQ node has a data directory that stores all the information that resides on that node. A data directory contains two types of data: definitions (metadata, schema/topology) and message store data.
Transactions in RabbitMQ concern only messages. So, it works only when a message is published or acknowledged. It's important to underline the acknowledged word because it doesn't mean the same as consumed. Strictly speaking, when a message is rolled back after acknowledging, it's not requeued.
The default for messaging is RabbitMQ and the default database is MySQL. They are known workhorses in the industry and in our experience they generally suffice even for large deployments in terms of scalability. In theory, any database supporting SQLAlchemy will do, but most users stick with the default.
Message ordering guaranteesRabbitMQ offers stronger guarantees since release 2.7. 0. Messages can be returned to the queue using AMQP methods that feature a requeue parameter (basic. recover, basic.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With