Шаблоны для реализации транзакций вне базы данных

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

Прежде чем я начну разворачивать свой собственный объектный механизм ACID, есть ли какие-либо общепринятые шаблоны для реализации семантики ACID на уровне объекта?

А еще лучше, есть ли какие-нибудь библиотеки, которые я могу использовать для платформы .NET?

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

Ответов (7)

Решение

В последний раз я видел нечто подобное несколько лет назад. Немногое, что я помню об этом, это то, что он использовал шаблон команды и сохранял каждый объект команды в очереди. Я думаю, это был стек LIFO.

Таким образом, если «транзакция» не удалась, движок выскочит из командного объекта, отменит команду, а затем уничтожит командный объект. Повторяйте, пока стопка не станет пустой. Стек очищается, если «транзакция» прошла успешно.

К сожалению, больше я не помню.

CSLA.NET реализует аналогичный стек отмены. Это единственный пример с кодом, который я могу придумать.

Windows Workflow Foundation имеет концепцию компенсации (с использованием составного действия), когда семантика ACID может быть неподходящей ... Конечно, она также поддерживает транзакции ACID.

Хороший вопрос: зачем возиться с компенсацией? Разве одна большая ACID-транзакция с автоматическим откатом не так хороша? Транзакция ACID является наиболее подходящей, когда операции происходят в одной базе данных или в одной информационной системе. Это также наиболее уместно, когда операции заканчиваются быстро. Когда задействованы разные компании и службы, определение процесса с точки зрения семантики ACID часто бывает сложной задачей. Чтобы он был изолированным и долговечным, вы должны держать все ресурсы разных компаний заблокированными на время выполнения задачи. Часто это неразумно, особенно если задача долгая. Чтобы он был последовательным и атомарным, вам понадобится специальный код компенсации.

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

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

Для необратимых операций, таких как доступ к веб-службе или отправка электронного письма, вам необходимо использовать компенсирующие транзакции : сделать еще один вызов веб-службы, чтобы отменить или обновить результаты предыдущего, или, возможно, отправить другое электронное письмо, уведомляющее получателя о том, что что-то не так. t работать по назначению.

Две мысли:

Поскольку вы не можете отменить отправку электронной почты, а написать файл относительно недорого, я бы просто сделал это в правильном порядке:

  1. Попробуйте записать файл / записать файл. В случае неудачи остановитесь, в противном случае продолжайте:
  2. Позвоните в веб-службу. В случае неудачи удалите файл и остановитесь, в противном случае продолжайте:
  3. Отправка электронной почты - электронная почта в любом случае является асинхронной, поэтому вы никогда не узнаете, было ли она отправлена ​​или нет, поскольку большинство серверов электронной почты настроены на повторение в течение нескольких дней, если возникает ошибка, и вы никогда не получите подтверждение что электронное письмо прошло, даже если оно было успешным.

One idea is to use JMS as the 'engine' and you can utilize JMS transactions (which can join existing transactions e.g. DB transaction). This starts to lead towards an async event-driven architecture which is probably a good thing unless we are talking simple apps - in which case you probably don't need to be asking the question.

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

Вы не должны помещать код отправки электронной почты в транзакцию, потому что даже если вы можете отправить электронное письмо, фиксация транзакции db может завершиться ошибкой по той или иной причине. Вы также не должны помещать отправку электронной почты вне транзакции (после фиксации), потому что отправка электронной почты может не сработать, что приведет к потере учетной записи.

So to use JMS in this scenario - put JMS sending code within the DB transaction and have it join that transaction. You are guaranteed message delivery. On the other end have something consuming the queue sending emails out. In case of email send failure, best option is to log/raise an alert - JMS will roll back and put message back into the queue for later consumption. i.e. to try and resend email once you have hopefully fixed whatever the issue is.

The critical thing is - DB record is consistent and email is eventually sent.

Также недавно был выпущен экспериментальный проект STM .NET . Этот проект добавляет в C# память транзакций. Фактически он модифицирует CLR для поддержки этого.