2009-02-24 68 views
9

我必须发送电子邮件,写入文件并调用Web服务。为了保持一致性,所有步骤都必须发生。如果任何步骤抛出异常或错误,则必须回退所有步骤。用于在数据库之外实现事务的模式

我走我自己的滚动对象ACID引擎之前,是否有对象级别实现ACID语义的普遍接受的模式?

更好的是,是否有任何现有的库可用于.NET平台?

编辑︰我知道发送电子邮件不能撤消,但未能连接到SMTP服务器是导致杀死整个交易。此外,我希望这可以扩展用于未来的行动。

+0

如何回滚电子邮件? – mbeckish 2009-02-24 03:23:44

+0

也许发送召回:) – 2009-02-24 03:24:54

回答

5

我最后一次看到这样的事情是几年前。我记住的一点是,它使用命令模式并将每个命令对象存储在一个队列中。我认为这是一个LIFO堆栈。

因此,如果“交易”失败时,发动机就会关闭弹出一个命令对象,撤消命令,然后销毁命令对象。重复,直到堆栈为空。如果“交易”成功,则堆栈被清除。

不幸的是,我不记得比这更多。

CSLA.NET实现了一个类似的撤消堆栈。这是代码的唯一例子,我可以从头脑中思考。

3

当ACID语义可能不合适时,Windows Workflow Foundation的概念为compensation(使用复合活动)。当然,它也支持ACID事务。

一个好问题是为什么要用 赔偿?是不是一个大的ACID 交易与自动回滚 一样好?当在相同的数据库中或在相同的信息系统内发生操作 时,ACID事务是最合适的 。当操作快速结束 时,它也是最合适的 。当涉及不同的公司和服务时,根据ACID语义 定义 过程通常是具有挑战性的。因为它是孤立和耐用的 ,您必须保留 不同公司 的所有资源在任务期间锁定。 这通常是不合理的,特别是如果任务很长的话。对于它 是一致的和原子的,您需要 临时补偿代码。

1

由于无法取消发送一封电子邮件,这是相对便宜写一个文件,我只是做这些事情按正确的顺序:

  1. 尝试写入文件/写入文件。如果不成功,请停止,否则继续:
  2. 调用Web服务。如果不成功,请删除文件并停止,否则继续:
  3. 发送电子邮件 - 电子邮件是异步的,所以你永远不会知道它是否被发送,因为大多数电子邮件服务器都设置为重试几天后如果发生错误,并且您永远不会收到确认电子邮件已经通过,即使它已成功
3

不依赖外部库的最简单的技术是prevalence。定期使用检查点通过使用序列化来拍摄您的状态快照,然后通过序列化足够的信息对您的数据进行每次有效操作以便稍后重复,从而维护日志。如果有事情发生,请重新加载最近的检查点,然后重新应用该点后写入的所有日志记录。

对于更复杂的东西,请尝试software transactional memory。在当前语言中实现可能有点笨拙,但功能非常强大,并且可能会给你一些额外的并发技术。

对于不可逆转的操作,如访问Web服务或发送电子邮件,您需要使用compensating transactions:进行另一个Web服务调用以取消或更新前一个Web服务的结果,或者发送另一封电子邮件,通知收件人事情没有按预期工作。

1

一个思路归结为简单的(但仍然很有用)的建议是使用JMS的“引擎”,你可以使用JMS交易(可加入现有的交易如数据库事务)。这开始导致异步事件驱动的架构,这可能是一件好事,除非我们正在谈论简单的应用程序 - 在这种情况下,你可能不需要问这个问题。

一个这样的例子就是简单的账户创建。为此,您需要将帐户信息持久保存到数据库,并向用户发送激活邮件 - 但您希望他们在同一事务中出于显而易见的原因。

您不应该在交易中放置电子邮件发送代码,因为即使您可能发送电子邮件--db事务提交可能因某种原因失败。 您也不应该在交易之外(提交后)发送电子邮件,因为电子邮件发送可能会导致孤儿帐户失败。

因此,在这种情况下使用JMS--将JMS发送代码发送到数据库事务中并让它加入该事务。你保证信息传递。另一方面有消耗队列发送电子邮件的东西。在电子邮件发送失败的情况下,最好的选择是记录/提出警报 - JMS将回滚并将消息放回队列供以后使用。即一旦您希望解决问题,就尝试重新发送电子邮件。

关键的是 - 数据库记录是一致的,最终发送邮件。

0

此外,最近发布了一个实验项目STM .NET。这个项目将事务内存添加到C#中。它实际上修改了CLR来支持这一点。