我们正在使用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());
}
答案 0 :(得分:3)
在NEventStore中,一致性边界是流。从版本3.2开始(正如@Marijn提到的那样,issue #159)在从所有持久性引擎中读取流时,CommitSequence列用于对CommitMessages(以及其中包含的EventMessages)进行排序。
基于每个流保证EventMessage订购。 流中的消息没有隐含的排序。由于选择的持久性引擎的某些方面可能发生的任何实际排序是偶然的,不得依赖。
保证跨流的排序将严重限制库的分布式友好方面。即使我们考虑这样的功能,也必须使用所有支持的持久性引擎,包括NoSQL存储。
如果您正在练习域驱动设计,其中每个流代表一个聚合根,并且您需要保证在两个或更多聚合中进行排序,这表明您的域模型中存在设计问题。
如果您的预测需要合并来自多个源(流)的值,您可以依赖于内部源的排序,但您需要灵活地订购源间。您还应考虑重复消息的可能性,尤其是在通过外部总线或队列进行重放时。
如果您尝试使用时间戳(CommitStamp)在接收方端重新排序多个流,那将是脆弱的。时间戳具有固定的分辨率(ms,tick等)。即使只有一个作家,事情仍然可能“同时发生”。
答案 1 :(得分:1)
Damian在数据库中添加了一个检查点列。这是在当前的主分支中。当使用GetFromCheckpoint(int)
重播事件时,结果是正确的。
答案 2 :(得分:0)
在数据库级别,虽然CommitStamp适用于过滤,但CommitSequence列是指导排序的列。
对于那些在你正在使用的任何版本的lib上的API调用的转换 - 我将把它作为练习留给你(或者如果你填写代码片段和/或提及也许其他人可以介入的版本)