可靠的事件日志(不是真正的事件源)

时间:2018-12-24 05:58:10

标签: transactions cqrs event-sourcing consistency eventual-consistency

我正在实现一个系统,该系统应该以一致的方式存储所有事件,但是同时,我想通过使用某种混合方法来保持一致性。尽管事件源的想法很明确,但是所有的变种都进入了ES日志,然后消费者创建了物化视图,但令我感到困扰的是,由于系统现在在整个系统范围内都是异步的。

CQRS + ES方法建议应在UI级别解决此问题,要求客户端等待。假设我有一些接近stackoverflow的东西。当我完成此问题后,我将点击“发布您的问题”按钮,该网站将立即带我回答我的问题。使用ES方法,单击“发布您的问题”将意味着我的问题将被推送到ES商店中,以便稍后进行处理,而我会看到“我们正在发布您的问题,请稍等”,对吗?那就是我要避免的。我想出了以下模式:

CreateQuestionCommand command
    = new CreateQuestionCommand(uint userId, string questionBody, string[] tags)
QuestionSavedEvent result
    = command.execute() // save to DB as usual, return events
saveToES(result) // ugghhhhh...

如果我忘记了saveToES,则ES日志会变得不一致并且几乎没有用。如果我们团队中的任何开发人员忘记了它,那么同样的事情-整个ES日志只是一堆东西。

这种方法是否可行?这样可以解决ES的缺点,即最终的一致性,并且仍然保留其始终可靠的事件日志的光明面。

我遇到了一些解决方案。

1。

  • 某些数据库将允许访问可能是事件源的提交日志,但是这种方法有些枯燥,因为读取提交日志并没有说明可能需要的应用程序业务逻辑,也就是说,您只是从提交日志中读取UPDATE users SET loggedIn = "2018-12-30 10:00:00" WHERE id = 1,在这种情况下,事件仅为UserUpdated,但在应用程序级别,事件可能会好得多UserLoggedIn

ebay使用的另一种方法(据说,我已经在一本关于ES的书中读过它,想不起来标题了,这样)

2。

  • 开始交易1
  • UPDATE domain.users SET loggedIn = "2018-12-30 10:00:00" WHERE id = 1
  • 开始交易2(内部trx)
  • INSERT INTO events.log SET event = "{name: UserLoggedIn, userId: 1}", timestamp = "2018-12-30 10:00:00"
  • 提交交易2
  • 提交交易1

然后另一个进程可以轮询此events.log,以将数据发布到ES存储中。 通过设计,应强制每个数据更改器返回一个event和一个指向transaction 1的指针,这是系统永远不会“忘记”将事件提交到数据库的方式。

第三种方法是使用需要分布式事务管理器的2PC(两阶段提交),以及消息代理和数据库对2PC的支持,由于这种限制,我什至没有考虑这个方向。

因此,我只是想知道,以您的经验,什么是使系统保持“老式”(同步)的最佳方法,同时又能确保可靠的事件同时记录? (是的,我想要两个世界的所有好东西:))

免责声明:我知道这个问题有多大,如果您因此认为这个问题不属于社区,请告诉我,我将其删除。

1 个答案:

答案 0 :(得分:1)

  

这种方法是否可行?这样可以解决ES的缺点,即最终的一致性,并且仍然保留其始终可靠的事件日志的光明面。

小心翼翼地做到这一点没有错-重要的是要确保事件和状态存储在同一事务中,因此也存储在同一数据库中。

沿着状态存储事件的想法已经存在了一段时间。您经常会发现围绕“域事件”的思想进行这种讨论,并使用一个聚集引发的事件来触发其他地方的行为。例如,请参见Udi Dahan在Reliable Messaging without Distributed Transactions上的演讲。

为此,我们删除了CQRS,取而代之的是,我们只有一个逻辑数据模型来将当前状态和事件历史记录一起存储。

哪个是 fine ?如果CQRS不能解决您遇到的问题,则不应使用它。

将读取模型与写入模型分开等同于缓存;为了获得好处,您必须解决two hard problems之一的缓存失效问题。