6

我们正在使用 CQRS + ES。ES 是 NEventStore(以前的 JOliver EventStore)。我们在不同的命令中有 2 个聚合。第二个 AR 的投影取决于读取模型中第一个 AR 投影写入的数据。问题是,当我们运行软件时,一切都进行得如此之快,以至于有时这两个聚合以相同的日期时间(CommitStamp 列)保存在事件存储中。重放事件时,我们从一开始就按 CommitStamp 列排序。但是,如果这两个流具有相同的 CommitStamp 并且顺序错误,则读取模型预测会出现异常。

知道如何解决这个问题吗?

================================

这是在 github https://github.com/NEventStore/NEventStore/issues/170关于这个问题的讨论

================================

编辑:这是我们目前重播事件的方式。我搜索了 GetFrom(...) 的工作原理,结果发现 commitstamp 列不用于排序。毕竟没有提交顺序。因此,如果我开始重播事件,它可能会从今天返回一个事件,下一个是 2 年前记录的事件,下一个等

public void ReplayEvents(Action<List<UncommittedEvent>> whatToDoWithEvents, DateTime loadEventsAfterDate)
    {
        var eventPortion = store.Advanced.GetFrom(loadEventsAfterDate);

        var uncommitedEventStream = new UncommittedEventStream();
        foreach (var commit in eventPortion)
        {
            foreach (var eventMessage in commit.Events.ToList()))
            {
                uncommitedEventStream.Append(new UncommittedEvent(eventMessage.Body));
            }
        }
        whatToDoWithEvents(uncommitedEventStream.ToList());
    }
4

3 回答 3

3

在 NEventStore 中,一致性边界是流。从 3.2 版开始(如@Marijn 提到的问题 #159),当从所有持久性引擎的流中读取时,CommitSequence 列用于订购 CommitMessages(以及其中包含的 EventMessages)。

EventMessage 的顺序是保证在每个流的基础上进行的。流的消息没有隐含的顺序。由于所选持久性引擎的某些方面而可能发生的任何实际排序都是偶然的,不得依赖。

保证跨流排序将严重限制库的分布式友好方面。即使我们要考虑这样的特性,它也必须与所有支持的持久性引擎一起工作,包括 NoSQL 存储。

如果您正在实践领域驱动设计,其中每个流代表一个聚合根,并且您需要保证跨 2 个或更多聚合的排序,这表明您的领域模型中存在设计问题。

如果您的预测需要合并来自多个源(流)的值,您可以依赖源内排序,但您需要灵活地对源间进行排序。您还应该考虑重复消息的可能性,尤其是当您通过外部总线或队列重播时。

如果您尝试使用时间戳(CommitStamp)在接收端重新排序多个流,那将是脆弱的。时间戳具有固定的分辨率(毫秒、滴答等)。即使只有一个作家,事情仍然可能“同时”发生。

于 2013-07-08T13:07:59.187 回答
1

Damian 在数据库中添加了一个检查点列。这是在当前的主分支中。当事件重播GetFromCheckpoint(int)的结果是正确的。

于 2013-11-13T23:06:45.063 回答
0

在数据库级别,虽然 CommitStamp 适合过滤,但 CommitSequence 列应该指导排序。

至于你正在使用的任何版本的库的 API 调用,我将把它作为练习留给你(或者如果你填写代码片段和/或提及版本也许其他人可以介入)

于 2013-07-05T12:07:16.103 回答