Events 无事件源的CQR:处理事件日志失败

Events 无事件源的CQR:处理事件日志失败,events,domain-driven-design,microservices,cqrs,distributed-system,Events,Domain Driven Design,Microservices,Cqrs,Distributed System,由于在我的CQRS应用程序中不使用事件源,我引入了一个简单的事件日志,它使我能够更新读取存储 这意味着对我的应用程序的状态更改包括两个操作: 更新写入模型状态,例如SQL INSERT 将事件插入事件日志 两个写操作必须作为一个原子操作进行。不幸的是,事件日志驻留在另一个数据库中,因此我必须考虑分布式事务 大多数CQR样本都处理saga模式,它们似乎都利用了事件源,这使得事情变得更简单 我的问题是“半成品”状态更改,例如 SQL插入成功 事件日志插入失败 我可以想出一个补偿SQL操作(伪

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

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

  • 更新写入模型状态,例如SQL INSERT
  • 将事件插入事件日志
两个写操作必须作为一个原子操作进行。不幸的是,事件日志驻留在另一个数据库中,因此我必须考虑分布式事务

大多数CQR样本都处理saga模式,它们似乎都利用了事件源,这使得事情变得更简单

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

  • SQL插入成功
  • 事件日志插入失败
我可以想出一个补偿SQL操作(伪代码):

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

  • 每个事件都有一个序列号
  • 如果读取端复制检测到未处理的事件(例如,接收到40,然后是42),它将查询事件日志中的事件41
  • 如果事件41不可用,系统将停止复制任何事件,直到有人仔细查看
这需要手动维护,但可以防止读取数据库不同步

有真实的生活经历吗

两个写操作必须作为一个原子操作进行

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

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

这将为您提供一个远程事件日志,该日志始终与过去的某些状态保持一致,但不保证会一直跟踪到现在

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

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

严崔的讨论提出了这一点,而这一讨论又引用了凯蒂·麦卡弗里的观点:

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

实际上,在发出人工干预警报之前,重试次数应该有一个合理的上限


所以是的-您可以重试。

您的系统分布情况如何?您希望它具有多大的弹性/可扩展性?它每月应该能够处理大约1000万或2000万个事件。它应该能够使用多个节点处理总线系统连接的命令和/或事件。因此,事件复制可以在多个节点上进行。我非常喜欢本地事件日志的想法,这足以满足我的要求。非常感谢。
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?
}