事件采购和乐观并发控制

mar*_*iva 13 domain-driven-design optimistic-concurrency event-sourcing

当您希望代码在竞争条件下工作时,通常开发人员使用乐观并发控制(OCC).来自维基百科:

...在提交之前,每个事务都会验证没有其他事务修改了它已读取的数据.如果检查显示有冲突的修改,则提交事务将回退...

实现OCC的方法是检查version要修改的数据.如果版本不同,那么其他事务已修改数据,并由应用程序决定如何解决冲突(重新尝试,通知用户......).

草案如下:

class Repository
{
    public class save($data)
    {
        $currentVersion = $data->version;
        $data->version  = $currentVersion + 1;

        $result = $this->db->update($data, [
            'id'        => $data->id,
            'version'   => $currentVersion
        ]);

        if (1 === $result) {
            // everything ok
        } else {
            // conflict!
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是,因为EventSourcing我们只追加域中发生的所有事件,我们不能再使用这种方法来实现OCC.在使用EventSourcing时,哪些其他方法可以保留OOC?

一个可行的选项,它可以在存储时查找冲突事件.这种方法允许对事件进行细粒度控制.我不知道这是否会使解决方案复杂化,或者我认为这是一个"标准",我在http://danielwhittaker.me/2014/09/29/handling-concurrency-issues-cqrs-event-sourced指出-系统/

问题描述中的任何空白都是值得赞赏的.提前致谢!

gui*_*e31 12

尝试将事件附加到流时,您可以指定预期的当前版本号,并在实际当前数字与其匹配时抛出.

这种乐观并发机制内置于某些事件存储系统中.

您链接到的文章似乎描述了一种类似的方法,但更强大,因为您可以访问自预期版本以来发生的事件类型,并且可以根据更细粒度的标准检测冲突.


Anu*_*nov 6

默认情况下,乐观锁不适用于事件源,因为乐观锁需要锁定状态。在事件溯源中,您没有状态之类的东西,您只有事件列表(更改流)。正如已经多次提到的,您可以使用以下方法:

  1. 每个事件都有修订号(这对于事件溯源方法中的事件很常见)
  2. 每次当任何事件发生并且您需要保存它时,您应该从事件存储中获取最后的修订号
  3. 在将其保存到事件存储之前,只需增加此修订号
  4. 确保您在事件存储中拥有修订号的唯一索引
  5. 如果两个并行事务增加了最后的修订号(例如 5),并尝试保存两个具有增加的修订号的新事件(在我们的例子中为 6),其中一个将因唯一索引而失败。

另请记住,您的事件存储应该允许您使用唯一索引,在这种情况下,RDBMS 表是最佳选择。


小智 5

应用事件溯源,您的表中还应该有一个版本字段,否则在构建聚合根时无法获取事件的顺序。但顺序很重要。您还可以利用此版本字段来支持 OCC。例如,如果您有竞争条件,例如两个具有相同 ID 和相同版本的事件几乎同时保存在事件存储中,则最后一个事件会丢失,并且如果您使用复合,则会引发重复键异常主键由聚合 ID 和版本组成。