【发布时间】:2018-12-24 05:58:10
【问题描述】:
我正在实现一个系统,该系统应该以一致的方式存储所有事件,但同时我想通过使用某种混合方法来保持一致性。尽管事件溯源的概念很清楚,所有的突变都进入 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,由于这个限制,我什至没有研究这个方向。
所以,我只是想知道,根据您的经验,保持系统“老式”(同步)但同时保证可靠事件日志的最佳方法是什么? (是的,我想要来自两个世界的所有好东西:))
免责声明:我知道这个问题有多臃肿,如果您因此认为这个问题不属于社区,请告诉我,我会删除它。
【问题讨论】:
标签: transactions cqrs event-sourcing consistency eventual-consistency