在数据库外部实现事务的模式
我必须发送电子邮件、写入文件并调用网络服务。 为了保持一致性,所有步骤都必须发生。 如果任何步骤引发异常或错误,则必须回滚所有步骤。
在我开始使用自己的对象 ACID 引擎之前,是否有任何普遍接受的模式用于在对象级别实现 ACID 语义?
更好的是,是否有任何现有的库可以用于 .NET 平台?
编辑:我知道发送电子邮件无法撤消,但无法连接到 SMTP 服务器会导致整个事务终止。 另外,我希望它可以扩展以供将来的操作使用。
I have to send an email, write to a file, and call a web service. To maintain consistency, all steps must happen. If any step throws an exception or errors out, all steps must be rolled back.
Before I go rolling my own object ACID engine, are there any commonly accepted patterns for implementing ACID semantics at the object level?
Better yet, are there any existing libraries I can use for the the .NET platform?
Edit: I know sending an email can't be undone, but failing to connect to the SMTP server is cause to kill the whole transaction. Also, I'd like this to be extensible for use with future actions.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我上次看到这样的事情已经是几年前的事了。 我记得的一点是它使用命令模式并将每个命令对象存储在队列中。 我认为这是一个 LIFO 堆栈。
因此,如果“事务”失败,引擎将弹出命令对象,撤消该命令,然后销毁该命令对象。 重复直到堆栈为空。 如果“事务”成功,堆栈将被清除。
不幸的是,我不记得更多了。
CSLA.NET 实现了类似的撤消堆栈。 这是我能立即想到的唯一一个包含代码的示例。
The last time I saw something like this was several years ago. The little bit that I remember about it is that it was using the command pattern and storing each command object in a queue. I think it was a LIFO stack.
So if the "transaction" failed, the engine would pop off a command object, undo the command, then destroy the command object. Repeat until the stack was empty. The stack got cleared if the "transaction" was successful.
Unfortunately, I don't remember more than that.
CSLA.NET implements a similar undo stack. That's the only example with code that I can think off the top of my head.
Windows Workflow Foundation 有一个补偿(使用复合活动)的概念ACID 语义可能不合适。当然,它也支持 ACID 事务。
Windows Workflow Foundation has a concept of compensation (using Composite activity) when ACID semantics might not be appropriate..Off course, it has support for ACID transactions as well.
不严重依赖外部库的最简单技术是流行。 通过使用序列化定期检查点来拍摄状态快照,然后通过序列化针对数据的每个副作用操作的足够信息来维护日志,以便稍后重复。 如果出现问题,请重新加载最近的检查点,然后重新应用该点之后写入的所有日志记录。
对于更复杂的东西,请尝试软件事务内存。 用当前的语言实现可能有点笨拙,但功能非常强大,并且还可以为您提供一些额外的并发技术。
对于访问 Web 服务或发送电子邮件等不可逆转的操作,您需要使用补偿事务:进行另一个 Web 服务调用以取消或更新前一个的结果,或者可能发送另一封电子邮件,通知收件人事情没有按预期进行。
The simplest technique without relying heavily on an external library is prevalence. Periodically checkpoint by using serialization to take a snapshot of your state, then maintain a journal by serializing enough information on every side-effectful operation against your data to repeat it later. If something blows up, reload the most recent checkpoint, then re-apply all journal records written after that point.
For something more sophisticated, try software transactional memory. It may be somewhat clumsy to implement in current languages, but is quite powerful and may give you some additional concurrency techniques as well.
For irreversible operations like accessing a Web service or sending an email, you'll need to use compensating transactions: make another Web service call to cancel or update the results of the previous one, or perhaps send another email advising the recipient that things didn't work as intended.
由于您无法取消发送电子邮件,并且写入文件相对便宜,因此我只会按正确的顺序执行这些操作:
Since you can't un-send an e-mail, and it's relatively inexpensive to write a file, I'd just do those things in the proper order:
一种想法是使用 JMS 作为“引擎”,您可以利用 JMS 事务(它可以加入现有事务,例如数据库事务)。 这开始导致异步事件驱动的架构,这可能是一件好事,除非我们谈论的是简单的应用程序 - 在这种情况下,您可能不需要问这个问题。
简单的帐户创建就是一个例子。 为此,您希望将帐户信息保存到数据库,并向用户发送一封电子邮件以进行激活 - 但由于显而易见的原因,您希望它们处于同一事务中。
您不应该将电子邮件发送代码放入事务中,因为即使您可以发送电子邮件 - 数据库事务提交也可能会由于某种原因而失败。
您也不应该将电子邮件发送放在事务之外(提交后),因为电子邮件发送可能会失败,从而导致孤立帐户。
因此,要在这种情况下使用 JMS,请将 JMS 发送代码放入数据库事务中,并让它加入该事务。 保证您的消息传递。 另一端有一些东西消耗队列发送电子邮件。 如果电子邮件发送失败,最好的选择是记录/发出警报 - JMS 将回滚并将消息放回到队列中以供以后使用。 即,一旦您希望解决问题,就尝试重新发送电子邮件。
关键是——数据库记录一致并且电子邮件最终发送。
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.
An example of this would be simple account creation. For this you want to persist account information to the DB and also send the user an email for activation - but you want them in the same transaction for obvious reasons.
You should not put email sending code within the transaction because even though you may send the email - db transaction commit may fail for one reason or another.
You also should not put email sending outside of the transaction (after commit) because email sending may fail leading to an orphan account.
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.
两个想法:
Two thoughts:
此外,最近还发布了一个实验项目 STM .NET。 该项目向 C# 添加了事务内存。 它实际上修改了 CLR 以支持这一点。
Also, A experimental project STM .NET was recently released. This projects adds transaction memory to C#. It actually modifies the CLR to support this.