DynamoDB - Магазин событий на AWS - PullRequest
0 голосов
/ 19 апреля 2019

Я проектирую Event Store на AWS и выбрал DynamoDB, потому что это казалось лучшим вариантом. Мой дизайн выглядит неплохо, но я столкнулся с некоторыми проблемами, которые не могу решить.

** Дизайн

События однозначно идентифицируются парой (StreamId, EventId):

  • StreamId: то же самое, что и для aggregateId, что означает один Поток событий для один Агрегат.
  • EventId: инкрементное число, которое помогает сохранить порядок в том же потоке событий

События сохраняются в DynamoDb. Каждое событие отображается на одну запись в таблице, где обязательными полями являются StreamId, EventId, EventName, Payload (можно легко добавить больше полей).

PartitionKey - это StreamId, sortKey - это EventId.

Оптимистическая блокировка используется при записи события в поток событий. Для этого я использую условные записи DynamoDb. Если событие с таким же (StreamId, EventId) уже существует, мне нужно пересчитать агрегат, перепроверить бизнес-условия и, наконец, записать заново, если бизнес-условия пройдут.

Потоки событий

Каждый поток событий определяется ключом partitionKey. Запрос потока для всех событий равен запросу для partitionKey = $ {streamId} и sortKey между 0 и MAX_INT.

Каждый поток событий идентифицирует один и только один агрегат. Это помогает обрабатывать параллельные записи в одном агрегате с использованием оптимистической блокировки, как описано выше. Это также обеспечивает высокую производительность при повторном вычислении агрегата.

Публикация событий

События публикуются с использованием комбинации DynamoDB Streams + Lambda.

Воспроизведение событий

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

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

Я что-то упустил? Или мой дизайн просто неправильный?

Ответы [ 3 ]

1 голос
/ 22 апреля 2019

Вы можете использовать GSI для извлечения событий за определенный период времени.В зависимости от количества обрабатываемых событий, вам может потребоваться написать GSI-код, чтобы избежать горячих клавишПредполагая, что элементы событий меньше 1 КБ, вам нужно будет распределить их по GSI, если скорость загрузки выше 1000 элементов / сек.Если события больше 1 КБ, вам нужно будет распространять их больше.Для элементов размером менее 1 КБ возьмите общее количество событий в секунду и разделите на 1000. Это скажет вам, сколько шардов GSI необходимо для того, чтобы не отставать от таблицы, например, если вы принимаете 5 КБ событий в секунду, вам потребуется 5 шардов.

Когда вы записываете события в таблицу, добавьте новый атрибут с именем «GSIKey» и создайте случайное значение от 0 до 4 для этого атрибута при вставке событий.Создайте GSI, используя «GSIKey» в качестве ключа раздела и метку времени в качестве ключа сортировки.Когда вам нужно получить все события в заданном временном диапазоне, запросите все 5 фрагментов с нужным временным диапазоном, а затем просто объедините сортировку наборов результатов, чтобы получить упорядоченный по времени список событий.Если вы обрабатываете менее 1000 событий в секунду, вы можете использовать «0» в качестве значения GSIKey и просто запросить этот раздел для нужных вам событий.

1 голос
/ 19 апреля 2019

Я что-то упустил?

Не совсем; это трудная проблема [тм].

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

Обычно это работает так: ваше хранилище не только сохраняет записанные изменения, но также index , который поддерживает чтение. Например, Хранилище сообщений Eventide postgres зависит от индексации, которая происходит при вставке строк в таблицу. В случае Event Store обновления индекса записываются как часть той же сериализованной «транзакции», что и изменения в потоке (ах).

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

Уберите координацию, и у вас есть нечто аналогичное назначению уникального хоста для каждого потока.

Может быть полезно внимательно взглянуть на базу данных объектов Git и ознакомиться с тем, что на самом деле происходит в этом магазине под обложками. Я также обнаружил, что выступление Рича Хики Язык системы дало полезные понятия для отличия values от names от references.

Я выбрал DynamoDB, потому что он показался лучшим вариантом

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

0 голосов
/ 23 апреля 2019

Оригинальный ответ : Не могли бы вы рассказать о «совокупности»? Это то же самое, что и EventID, или это отдельный атрибут элемента?

Вам нужно хранить события и совокупность?

Каковы требования к долговечности вашего мероприятия?

Если <14 дней, будет ли для вас вариант хранения событий в Кинезисе? Как отметил Рик Хулихан, в вашем проекте могут возникнуть проблемы с горячими разделами или горячими клавишами, что потребует увеличения количества блоков RCU / WCU в таблице DynamoDB. Кинезис решает это. С его помощью вы сможете сосредоточиться на логике своего приложения. </p>

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

Обновление 4/23 :

Позвольте мне предложить вам другую альтернативу: CloudWatch Logs. Группа журналов CloudWatch будет эквивалентна вашим таблицам событий. Каждый из ваших потоков будет сопоставлен с потоком журнала CloudWatch.

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

Преимущество CWL в том, что вы избежите проблем с горячими клавишами, которые были упомянуты выше. Недостатки: (1) Вам нужно будет продумать решение против CWL. (2) DynamoDB предлагает задержку P99 <10 мс для чтения и <20 мс для записи. Операции чтения и записи в CWL намного выше (например, от 10 до 100 или мс). </p>

Надеюсь, это немного поможет.

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