Магазин событий и оптимистичный параллелизм - PullRequest
0 голосов
/ 13 сентября 2018

Грег Янг в своем документе о CQRS в разделе «Построение хранилища событий» , при записи событий в хранилище событий он проверял наличие оптимистичного параллелизма.Я не совсем понимаю, почему он сделал эту проверку, может кто-нибудь объяснить мне на конкретном примере.

Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 13 сентября 2018

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

Способ использования хранилища событий выглядит следующим образом:

  1. Старые события загружаются из Eventstream (= раздел в хранилище событий, который содержит все события, сгенерированные экземпляром Aggregate)
  2. Старые события обрабатываются / применяются Агрегатом, которому они принадлежат, в порядке их создания.
  3. Агрегат, основанный на внутреннем состоянии, которое было построено из этих событий, решает испустить некоторые новые события
  4. Эти новые события добавляются в Eventstream

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

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

0 голосов
/ 13 сентября 2018

Я не совсем понимаю, почему он сделал эту проверку, может кто-нибудь объяснить мне конкретный пример.

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

Если база данных будет поддерживать более одного потока выполнения, имеющего доступ на запись, то вы столкнулись с проблемой «потерянного обновления».Отрисованные как связанные списки, это может выглядеть следующим образом:

Thread(1) [... <- 69726c3e <- /x.tail] = get(/x)
Thread(2) [... <- 69726c3e <- /x.tail] = get(/x)
Thread(1) set(/x, [ ... <- 69726c3e <- 709726c3 <- /x.tail])
Thread(2) set(/x, [ ... <- 69726c3e <- 83b97195 <- /x.tail])

История, записанная потоком (2), не включает событие: 709726c3, записанное потоком (1).Таким образом, «потерянное обновление».

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

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

Единственная изменяемая часть изменения - единственное место, где мы заменяем перезапись старого значения новым значением - это когда мы меняем /x.tail

Thread(1) [... <- 69726c3e <- /x.tail] = get(/x)
Thread(2) [... <- 69726c3e <- /x.tail] = get(/x)
Thread(1) set(/x, [ ... <- 69726c3e <- 709726c3 <- /x.tail])
Thread(2) set(/x, [ ... <- 69726c3e <- 83b97195 <- /x.tail])

Проблема здесь в том, что Thread (2) считал 6 <- /x.tail истинным и заменил его значением, потерявшим событие 7. Если мы изменим нашу запись с set на compare-and-set ...

Thread(1) [... <- 69726c3e <- /x.tail] = get(/x)
Thread(2) [... <- 69726c3e <- /x.tail] = get(/x)
Thread(1) compare-and-set(/x, 69726c3e <- /x.tail, [ ... <- 69726c3e <- 709726c3 <- /x.tail])
Thread(2) compare-and-set(/x, 69726c3e <- /x.tail, [ ... <- 69726c3e <- 83b97195 <- /x.tail]) // FAILS

, тогда хранилище данных может обнаружить конфликт и отклонить недопустимую запись.

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

Thread(1) [... <- 69726c3e <- /x.tail] = get(/x)
Thread(2) [... <- 69726c3e <- /x.tail] = get(/x)
Thread(2) compare-and-set(/x, 69726c3e <- /x.tail, [ ... <- 69726c3e <- 83b97195 <- /x.tail])
Thread(1) compare-and-set(/x, 69726c3e <- /x.tail, [ ... <- 69726c3e <- 709726c3 <- /x.tail]) // FAILS

Проще говоря, где set дает нам семантику «выиграл последний писатель», compare-and-set дает нам «выигрыш первого писателя», чтоустраняет потерянную проблему обновления.

...