Интеграция RabbitMQ с транзакциями в базе данных

Представьте себе ситуацию:

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();

Теперь потребитель EntryUpdatedMessage может получить это сообщение до того, как транзакция txn будет зафиксирована и, следовательно, не сможет увидеть обновление.

Теперь я знаю, что RabbitMQ действительно поддерживает транзакции сам по себе, но мы не можем их использовать, потому что мы создаем новый IModel для каждой публикации и имеющий для каждого потока модель действительно громоздка в нашем сценарии (веб-сайт ASP.NET приложение).

Я думал о наличии списка сообщений, которые должны публиковаться при транзакции БД, но это действительно вонючее решение.

Каков правильный способ решения этой проблемы?

Ответ 1

RabbitMQ рекомендует вам использовать подтверждения издателя, а не транзакции. Сделки не работают хорошо.

В любом случае транзакции обычно не очень хорошо работают с сервис-ориентированной архитектурой. Лучше принять подход "в конечном счете последовательный", когда отказ может быть повторен позже, а дублированные сообщения идемпотента игнорируются.

В вашем примере я бы сделал обновление базы данных и зафиксировал ее перед публикацией сообщения. Когда издатель подтвердит возврат, я бы обновил поле в записи базы данных, чтобы указать, что сообщение было отправлено. Затем вы можете запустить процесс подметания, проверить наличие неотправленных сообщений и отправить их на своем пути. Если сообщение прошло, но по какой-либо причине подтверждение или последующая запись базы данных не удались, вы получите дублирующее сообщение. Но это не имеет значения, потому что вы разработали свои сообщения как идемпотентные.