我正在尝试使用与我所看到的各种 Greg Young 启发示例一致的实践和原则来实施事件溯源系统。
我了解版本检查逻辑是如何工作的,并且在保存聚合时,如果当前版本与预期版本不匹配,则意味着另一个会话/客户端应用程序在您之前更新了聚合。
我也明白,当并发事件被保存时,您可以采用一种追溯解决冲突的方法,这个问题与这样做无关。
我想了解的是,在使用诸如 ravendb 之类的 nosql 数据库作为事件存储的特定实现中,我将如何确保写入的事件不会由于竞争条件而与版本号重叠。
以下代码来自一个示例项目来说明:
在Repository<TAggregate>
存储库类中有一个保存方法
public void Save(AggregateRoot aggregate, int expectedVersion)
{
if (aggregate.GetUncommittedChanges().Any())
{
lock (_lockStorage)
{
var item = new T();
if (expectedVersion != -1)
{
//issue if two processes get to this line of code below together
//and both have the same 'version to be expected' then start writing together
item = GetById(aggregate.Id);
if (item.Version != expectedVersion)
{
throw new ConcurrencyException(string.Format("Aggregate {0} has been previously modified",
item.Id));
}
}
_storage.Save(aggregate);
}
}
}
现在,当只有一个应用程序时,这基本上可以正常工作。当当前线程获取锁,检查版本,然后写入自己的事件时,锁会停止任何其他线程向事件存储写入事件。
然而,想象两个独立的客户端在不同的机器上运行。显然,这两个进程都可以一起进入锁,然后它们都可以执行该GetById()
方法并且都看到相同的当前提交的版本。然后,两个进程将继续写入具有递增版本号的未提交事件。但是,这会使聚合的事件流处于可能存在具有相同版本号的不同事件的状态。
我知道我可以做一些回顾性的决议,但这不是我想要完成的。
现在很明显,这意味着需要在事件存储数据库方面进行某种锁定。谁能建议如何做到这一点?答案不必是特定于数据库的,但 ravendb 示例会很棒,因为这就是我计划用它来原型化我的事件源系统。