我尝试使用符合我见过的各种Greg Young灵感示例的实践和原则来实施事件采购系统。
我理解版本检查逻辑是如何工作的,并且在保存聚合时,如果当前版本与预期版本不匹配,则意味着另一个会话/客户端应用程序在您拥有之前更新了聚合。
我也理解你可以在保存并发事件时采用一种回顾性解决冲突的方法,这个问题不是关于这样做的。
我想要了解的是,在使用nosql数据库(如ravendb)作为事件存储的特定实现中,我如何确保写入的事件不会因竞争条件而重叠版本号。
示例项目中的以下代码说明:
在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()
方法,并且两者都看到相同的当前已提交的版本。然后,两个进程将继续使用递增的版本号来编写未提交的事件。但是,这会使聚合的事件流处于可能存在具有相同版本号的不同事件的状态。
我知道我可以做某种回顾性的解决方案,但这并不是我想要完成的事情。
现在显然这意味着需要在Event Store数据库端进行某种锁定。任何人都可以建议如何做到这一点?答案不一定是数据库特定的,但ravendb的例子很棒,因为我计划用我的事件采购系统原型。
答案 0 :(得分:1)
有没有理由在不同机器上运行的两个独立客户端无法通过API与应用程序交互?这样,事件的中央处理在1台机器上处理,避免了您描述的问题。如果您指的是部分连接的方案,那么您仍然不会在客户端上执行此逻辑。如果不可避免,您需要在某个时刻合并事件流,然后处理冲突。
如果它有用,我确实有关于处理concurrency issues的帖子,但它没有处理你的确切场景,但可能会给你一些关于并发冲突解决的想法。
答案 1 :(得分:1)
当底层数据库已经为此提供支持时,不要自己实现乐观并发控制。
通常,这是通过将您希望数据所在的版本传递给数据库服务器来实现的。只有当版本与当前版本匹配时,更新才会成功完成。
RavenDB使用电子标签进行乐观并发控制: