В последнее время я читал об источниках событий и мне действительно нравятся идеи, стоящие за ним, но я застрял со следующей проблемой.
Допустим, у вас есть N одновременных процессов, которые получают команды (например, веб-серверы), генерируют в результате события и сохраняют их в централизованном хранилище. Давайте также предположим, что все переходные состояния приложения поддерживаются в памяти отдельных процессов путем последовательного применения событий из хранилища.
Теперь предположим, что у нас есть следующее бизнес-правило: каждый отдельный пользователь должен иметь уникальное имя пользователя.
Если два процесса получают команду регистрации пользователя для одного и того же имени пользователя X, они оба проверяют, что X отсутствует в их списке имен пользователей, правило проверяется для обоих процессов, и они оба сохраняют событие «новый пользователь с именем пользователя X» в хранилище ,
Теперь мы вошли в противоречивое глобальное состояние, потому что нарушено бизнес-правило (есть два разных пользователя с одним и тем же именем пользователя).
В традиционной системе стилей СУБД N - сервера <-> 1 база данных используется в качестве центральной точки синхронизации, которая помогает предотвратить такие несоответствия.
Мой вопрос: как источники событий обычно подходят к этой проблеме? Они просто последовательно обрабатывают все команды (например, ограничивают количество процессов, которые могут записать в хранилище, до 1)?
источник
Ответы:
В системах с источником событий «хранилище событий» выполняет ту же роль. Для объекта, полученного из события, ваша запись - это добавление ваших новых событий к определенной версии потока событий. Таким образом, как и при параллельном программировании, вы можете получить блокировку этой истории при обработке команды. Для систем, основанных на событиях, чаще используется более оптимистичный подход - загрузить предыдущую историю, вычислить новую историю, а затем сравнить и заменить. Если какая-то другая команда также записала в этот поток, то сравнение и обмен не удаются. Оттуда вы либо повторно запустите свою команду, либо откажетесь от своей команды, или, возможно, даже включите свои результаты в историю.
Конфликт становится основной проблемой, если все N серверов с их командами M пытаются записать в один поток. Обычный ответ здесь состоит в том, чтобы выделить историю для каждого источника событий в вашей модели. Таким образом, пользователь (Боб) будет иметь отличную историю от пользователя (Алиса), и запись в один не будет блокировать записи в другой.
Грег Янг о проверке набора
Существует ли элегантный способ проверки уникальных ограничений на атрибуты объекта домена без перемещения бизнес-логики на уровень обслуживания?
Короткий ответ, во многих случаях более глубокое изучение этого требования, показывает, что (а) это плохо понятый прокси-сервер для какого-либо другого требования или (б) что нарушения «правила» допустимы, если их можно обнаружить (отчет об исключении) , смягчены в течение некоторого временного окна или являются низкочастотными (например, клиенты могут проверить, доступно ли имя, перед отправкой команды на его использование).
В некоторых случаях, когда ваше хранилище событий хорошо справляется с проверкой набора (например, реляционная база данных), вы реализуете требование путем записи в таблицу «уникальных имен» в той же транзакции, которая сохраняет события.
В некоторых случаях вы можете выполнить это требование только путем публикации всех имен пользователей в одном потоке (что позволяет оценивать набор имен в памяти как часть модели вашего домена). - В этом случае два процесса обновят попытку обновления истории потока, но одна из операций сравнения и обмена завершится неудачно, и повторная попытка этой команды сможет обнаружить конфликт.
источник
Похоже, вы могли бы реализовать бизнес-процесс (
saga
в контекстеDomain Driven Design
) для регистрации пользователя, где с пользователем обращаются как сCRDT
.Ресурсы
https://doc.akka.io/docs/akka/current/distributed-data.html http://archive.is/t0QIx
"CRDT с распределенными данными Akka" https://www.slideshare.net/markusjura/crdts-with-akka-distributed-data, чтобы узнать о
CmRDT
s - операционные CRDTCvRDT
s - CRTD на основе состоянияПримеры кода в Scala https://github.com/akka/akka-samples/tree/master/akka-sample-distributed-data-scala . Может быть, «корзина» наиболее подходит.
источник