Внедрение хранилища событий с оптимистичными одновременными проверками для нескольких клиентов

Я пытаюсь внедрить систему поиска событий, используя методы и принципы в соответствии с различными примерами, вдохновленными Грегом Янгом, которые я видел.

Я понимаю, как работает логика проверки версии, и что при сохранении агрегата, если текущая версия не соответствует ожидаемой версии, это означает, что другое сеансовое/клиентское приложение обновило агрегат раньше вас.

Я также понимаю, что у вас может быть метод ретроспективного разрешения конфликтов, когда параллельные события были сохранены, этот вопрос не столько об этом.

Я пытаюсь понять, что в конкретной реализации с использованием базы данных 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(), и оба видят одну и ту же текущую зафиксированную версию. Затем оба процесса продолжали записывать незафиксированные события с увеличивающимися номерами версий. Однако это оставляет поток событий для агрегата в состоянии, в котором могут быть разные события с одним и тем же номером версии.

Я знаю, что мог бы сделать какое-то ретроспективное решение, но это не то, чего я пытаюсь достичь.

Теперь ясно, что это означает, что какая-то блокировка должна быть сделана на стороне базы данных хранилища событий. Может ли кто-нибудь посоветовать, как это сделать? Ответ не обязательно должен быть специфичным для базы данных, но пример ravendb был бы отличным, так как это то, с чем я планирую создать прототип своей системы поиска событий.


person Mark Kennedy    schedule 17.11.2014    source источник


Ответы (2)


Есть ли причина, по которой два отдельных клиента, работающих на разных машинах, не могут взаимодействовать с приложением через API? Таким образом, центральная обработка событий обрабатывается на 1 машине и позволяет избежать описанных вами проблем. Если вы имеете в виду сценарий с частичным подключением, вы все равно не будете выполнять эту логику на клиенте. Если это неизбежно, вам нужно будет в какой-то момент объединить потоки событий, а затем обрабатывать конфликты.

Если это будет полезно, у меня есть сообщение об обработке проблемы параллелизма, но он не обрабатывает ваш конкретный сценарий, но может дать вам некоторые идеи для разрешения конфликта параллелизма.

person Codescribler    schedule 18.11.2014

Не реализуйте оптимистичный контроль параллелизма самостоятельно, если базовая база данных уже поддерживает его.

Обычно это реализуется путем передачи версии, в которой вы ожидаете данные, на сервер базы данных. Только когда версия совпадает с текущей версией, обновление пройдет успешно.

RavenDB использует электронные теги для оптимистичного управления параллелизмом:

person Martijn van den Broek    schedule 21.03.2015
comment
Будет ли составное UNIQUE KEY CONSTRAINT для AggregateId AND Version выполнять то, о чем вы говорите? - person Cristian E.; 28.04.2018