【问题标题】:CQRS without Event Sourcing: handle event log failure没有事件源的 CQRS:处理事件日志失败
【发布时间】:2018-02-22 07:16:05
【问题描述】:

由于我没有在我的 CQRS 应用程序中使用事件溯源,因此我引入了一个简单的事件日志,使我能够更新读取存储。

这意味着我的应用程序的状态更改包含两个操作:

  • 更新写入模型状态,例如SQL 插入
  • 将事件插入事件日志

两个写操作都必须作为一个原子操作发生。不幸的是,事件日志位于另一个数据库中,所以我必须考虑分布式事务。

大多数 CQRS 示例都处理 saga 模式,而且它们似乎都使用了事件溯源,这让事情变得更加简单。

我的问题是“半完成”状态更改,例如

  • SQL 插入成功
  • 事件日志插入失败

我可以想出一个补偿 SQL 操作(伪代码):

SQLTransaction.Commit(); // if this fails, all is fine. Nothing to revert
try 
{
    EventLog.Insert(event);
}
catch(Exception ex) 
{
    // Try to undo the SQL stuff.
    CompensatingSQLTransaction().Commit(); 
    // uh-oh! The commit fails!!

    // What now? Do a Retry?
}

有什么概念可以帮助我吗? 我考虑了以下方案来防止读取数据库不同步:

  • 每个事件都有一个序列号
  • 如果读取端复制检测到未处理的事件(例如,接收到 40,然后是 42),它会在事件日志中查询事件 41。
  • 如果事件 41 不可用,系统将停止复制任何事件,直到有人仔细查看。

这需要手动维护,但可以防止读取数据库不同步。

任何真实的生活经历?

【问题讨论】:

  • 您的系统分布如何?您希望它具有多大的弹性/可扩展性?
  • 它应该能够每月处理大约 10 或 2000 万个事件。它应该能够利用多个节点来处理由总线系统连接的命令和/或事件。所以事件复制可能发生在多个节点上。

标签: events domain-driven-design microservices cqrs distributed-system


【解决方案1】:

两个写操作都必须作为一个原子操作发生。

此时有一个非常重要的问题要提出:为什么?如果远程事件日志与记录簿不同步,业务的成本是多少?

如果您不需要同步,那么直接的方法是将事件日志的副本放入与写入模型相同的数据库中。 Udi Dahan 在Reliable Messaging Without Distributed Transactions 中讨论了这种方法。写入事务成功后,您可以将事件从 SQL 存储复制到远程事件日志。

这为您提供了一个远程事件日志,该日志始终与过去的一些状态一致,但不保证会赶上现在。

这通常已经足够了;毕竟,事件日志本身就是过去的快照,当事件日志的表示被复制给消费者时,记录簿可能会发生变化。

但如果这样做不行,您的选择是找到一个可以提供可接受折衷的分布式事务引擎,或者在远程写入失败时使用 sagas 撤消您对本地存储的更改。

Yan Cui 对saga pattern in aws 的讨论,又引用了 Caitie McCaffrey 的2015 talk on sagas in distributed systems,提出了这一点:

因为补偿动作也可能失败,所以我们需要能够重试它们直到成功,这意味着它们必须是幂等的。

在实践中,应该有一个合理的上限。在提醒人工干预之前重试次数。

所以是的 - 你重试。

【讨论】:

  • 我真的很喜欢关于本地事件日志的想法,这足以满足我的要求。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-31
相关资源
最近更新 更多