Реализация хранилища событий с оптимистичными проверками параллелизма на нескольких клиентах
Я пытаюсь внедрить систему источников событий, используя методы и принципы в соответствии с различными вдохновенными Грегом Яном примерами, которые я видел.
Я понимаю, как работает логика проверки версии и что при сохранении агрегата, если текущая версия не соответствует ожидаемой версии, это означает, что другое приложение сеанса / клиента обновило агрегат раньше, чем вы.
Я также понимаю, что у вас может быть метод ретроспективного разрешения конфликтов, когда параллельные события были сохранены, вопрос не столько в этом.
Я пытаюсь понять, что в конкретной реализации с использованием базы данных 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 был бы великолепен, поскольку именно с этим я планирую создать прототип моей системы поиска событий.
2 ответа
Есть ли какая-то причина, по которой два отдельных клиента, работающие на разных компьютерах, не могут взаимодействовать с приложением через API? Таким образом централизованная обработка событий обрабатывается на 1 машине и позволяет избежать проблем, которые вы описываете. Если вы имеете в виду частично связанный сценарий, вы все равно не будете выполнять эту логику на клиенте. Если это неизбежно, вам нужно будет объединить потоки событий в какой-то момент, а затем обработать конфликты.
Если это полезно, у меня есть пост по решению проблем параллелизма, но он не описывает ваш точный сценарий, но может дать вам некоторые идеи для разрешения конфликтов параллелизма.
Не реализуйте оптимистический контроль параллелизма самостоятельно, когда базовая база данных уже обеспечивает поддержку для этого.
Как правило, это реализуется путем передачи версии, в которой вы ожидаете данные, на сервер базы данных. Только когда версия соответствует текущей версии, обновление пройдет успешно.
RavenDB использует электронные теги для оптимистичного управления параллелизмом: