事件存储和乐观并发

问题描述 投票:3回答:2

Greg Young在"Building an event storage"部分关于CQRS的文档中,当他向事件存储区写事件时,他检查了乐观的并发性。我不明白为什么他做了那个检查,有人能用一个具体的例子向我解释。

先感谢您。

cqrs optimistic-concurrency event-store
2个回答
3
投票

TLDR;需要进行此并发检查,因为发出的事件取决于先前的事件。因此,如果有其他事件由另一个进程同时发出,那么必须重新做出决定。

使用Event存储的方式如下:

  1. 旧事件从Eventstream加载(= Eventstore中的一个分区,其中包含Aggregate实例生成的所有事件)
  2. 旧事件由它们按生成顺序拥有它们的Aggregate处理/应用
  3. Aggregate基于从这些事件构建的内部状态,决定发出一些新事件
  4. 这些新事件将附加到Eventstream

因此,第3步取决于执行此命令之前生成的先前事件。

如果由另一个进程并行生成的某些事件被附加到同一个事件流,那么这意味着所做的决定是基于错误的前提,因此必须通过重复从步骤1重新获取。


2
投票

我不明白为什么他做了那个检查,有人能用一个具体的例子向我解释。

事件存储应该是持久的,在某种意义上说,一旦你编写了一个事件,它就会在每次后续读取时都可见。因此,数据库中的每个操作都应该是追加。一个有用的心智模型是想一个单链表。

如果数据库要支持多个具有写访问权限的执行线程,那么您将面临“丢失更新”问题。绘制为链接列表,这可能如下所示:

Thread(1) [... <- 69726c3e <- /x.tail] = get(/x)
Thread(2) [... <- 69726c3e <- /x.tail] = get(/x)
Thread(1) set(/x, [ ... <- 69726c3e <- 709726c3 <- /x.tail])
Thread(2) set(/x, [ ... <- 69726c3e <- 83b97195 <- /x.tail])

线程(2)写的历史不包括由线程(1)记录的事件:709726c3。因此“丢失更新”。

在通用数据库中,您通常使用事务来管理它:一些魔法可以跟踪所有数据依赖关系,如果在尝试提交事务时前提条件不成立,则所有工作都将被拒绝。

但事件存储不使用需要支持一般情况的所有自由度 - 禁止对存储在数据库中的事件进行编辑,因为更改事件之间的依赖关系。

改变的唯一可变部分 - 这是我们用新值覆盖旧值的唯一地方 - 是我们改变/x.tail的时候

Thread(1) [... <- 69726c3e <- /x.tail] = get(/x)
Thread(2) [... <- 69726c3e <- /x.tail] = get(/x)
Thread(1) set(/x, [ ... <- 69726c3e <- 709726c3 <- /x.tail])
Thread(2) set(/x, [ ... <- 69726c3e <- 83b97195 <- /x.tail])

这里的问题很简单,Thread(2)认为6 <- /x.tail是真的,并将其替换为丢失事件7的值。如果我们将写入从set更改为compare-and-set ...

Thread(1) [... <- 69726c3e <- /x.tail] = get(/x)
Thread(2) [... <- 69726c3e <- /x.tail] = get(/x)
Thread(1) compare-and-set(/x, 69726c3e <- /x.tail, [ ... <- 69726c3e <- 709726c3 <- /x.tail])
Thread(2) compare-and-set(/x, 69726c3e <- /x.tail, [ ... <- 69726c3e <- 83b97195 <- /x.tail]) // FAILS

然后数据存储可以检测到冲突并拒绝无效写入。

当然,如果数据存储以不同的顺序看到线程的动作,那么失败的命令可能会改变

Thread(1) [... <- 69726c3e <- /x.tail] = get(/x)
Thread(2) [... <- 69726c3e <- /x.tail] = get(/x)
Thread(2) compare-and-set(/x, 69726c3e <- /x.tail, [ ... <- 69726c3e <- 83b97195 <- /x.tail])
Thread(1) compare-and-set(/x, 69726c3e <- /x.tail, [ ... <- 69726c3e <- 709726c3 <- /x.tail]) // FAILS

更简单地说,set给了我们“最后作家获胜”的语义,compare-and-set给了我们“第一作家胜利”,这消除了失去的更新问题。

© www.soinside.com 2019 - 2024. All rights reserved.