I'm trying to implement an event sourcing system using the practices and principles in line with the various Greg Young inspired examples I have seen.
I understand how the version checking logic works and that when saving the aggregate, if the current version does not match the expected version it means another session/client app has updated the aggregate before you have.
I also understand that you can have in place a method of retrospectively resolving conflicts when concurrent events have been saved, this question is not so much about doing this.
What I am trying to understand is that in a specific implementation with using a nosql database such as ravendb as the event store, how would I ensure that the events written never overlap version numbers due to the race condition.
The following code from an example project to illustrate:
In the Repository<TAggregate>
repository class there is a save method
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);
}
}
}
Now essentially this works fine when there is only a single application. The lock stops any other thread writing events to the event store while the current thread has taken the lock, checked the version, and then written its own events.
However imagine two separate clients running on different machines. Obviously the two processes can both get into the lock together and they can both then execute the GetById()
method and both see the same currently committed version. Both processes would then go on to write the uncommitted event(s) with incrementing version numbers. However this leaves the Event Stream for the aggregate in a state where there could be different events with the same version number.
I know I could do some kind of retrospective resolution but that isn't what i'm trying to accomplish.
Now clearly this means that some kind of locking needs to be done on the Event Store database side of things. Could anyone advise how to do this? Answer does not have to be database specific but a ravendb example would be great as that's what I'm planning to prototype my event sourcing system with.