Переход от CRUD к CQRS/ES

Обсуждаем, как правильно строить приложения
Ответить
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Переход от CRUD к CQRS/ES

Сообщение anton_z »

Доброго времени суток!

Хочу обсудить такой момент. Как переходить от старых CRUD-приложений к CQRS/ES? Вернее, как это сделать плавно и постепенно.

Например, имеется интернет-аукцион. Его долго пилили предыдущие разрабы. Хранятся все данные в MySQL в реляционной модели с денормализацией (чтоб она сдохла) для ускорения чтения.

Требуется сделать быстрый и качественный поиск по каталогу лотов. Поиск полнотекстовый да еще с фильтрами и фасетами. Для него решено использовать Elasticsearch. Также планируется устранить денормализацию в реляционной базе.

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

Основная трудность здесь - обеспечить консистентность реляционной базы и эластика.

Писать сразу и туда и туда - я думаю не вариант. Будет очень хрупко.

Индекстировать по крону - не подходит. Клиенты и руководство хотят чтоб было сразу или незаметно быстро.

Приходит на ум такое решение - писать в отдельную таблицу реляционной БД события изменений агрегата в одной транзакции с запросами на изменения самого агрегата. Другим потоком запрашивать необработанные события, отсортированные по autoincrement и применять их в Elasticsearch, затем ставить флаг, что событие было обработано. Такой вот Business Log в дополнение к состоянию.

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

Все это может показаться довольно костыльным, но другого тут придумать пока не смог.

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

Кто-нибудь делал что-то подобное? Что можете подсказать? Правильным путем иду?
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Переход от CRUD к CQRS/ES

Сообщение zelenin »

anton_z писал(а): 2017.01.24, 05:23 Доброго времени суток!

Хочу обсудить такой момент. Как переходить от старых CRUD-приложений к CQRS/ES? Вернее, как это сделать плавно и постепенно.

Например, имеется интернет-аукцион. Его долго пилили предыдущие разрабы. Хранятся все данные в MySQL в реляционной модели с денормализацией (чтоб она сдохла) для ускорения чтения.

Требуется сделать быстрый и качественный поиск по каталогу лотов. Поиск полнотекстовый да еще с фильтрами и фасетами. Для него решено использовать Elasticsearch. Также планируется устранить денормализацию в реляционной базе.

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

Основная трудность здесь - обеспечить консистентность реляционной базы и эластика.

Писать сразу и туда и туда - я думаю не вариант. Будет очень хрупко.

Индекстировать по крону - не подходит. Клиенты и руководство хотят чтоб было сразу или незаметно быстро.

Приходит на ум такое решение - писать в отдельную таблицу реляционной БД события изменений агрегата в одной транзакции с запросами на изменения самого агрегата. Другим потоком запрашивать необработанные события, отсортированные по autoincrement и применять их в Elasticsearch, затем ставить флаг, что событие было обработано. Такой вот Business Log в дополнение к состоянию.
все выглядит логичным, но это не ES, а соответственно, если состояние не зависит от событий, то некоторые события будете терять (забывать тригеррить в коде).
anton_z писал(а): 2017.01.24, 05:23Порядок следования событий в рамках одного агрегата планирую обеспечить следующим образом. В таблице событий будет автоинкрементная колонка и колонка с идентификатором агрегата. Так как запись эту таблицу будет производиться после выполнения запроса к таблице агрегата - блокировка InnoDB не даст сгенерировать следующий идентификатор для события, пока предыдущая транзакция на агрегат не завершится.
смутно понял о чем речь. подкину лишь пару тезисов: а) id события можно генерить на клиенте в виде uuid, б) событиям можно сделать поле version, которое будет увеличиваться на клиенте, обеспечивать порядок событий и избавит от конкурентности.
anton_z писал(а): 2017.01.24, 05:23Кто-нибудь делал что-то подобное? Что можете подсказать? Правильным путем иду?
около того
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Переход от CRUD к CQRS/ES

Сообщение samdark »

Также планируется устранить денормализацию в реляционной базе.
А потом денормализовать ещё раз. Дело обычное. Схема устаревает так же, как и код.
Приходит на ум такое решение - писать в отдельную таблицу реляционной БД события изменений агрегата в одной транзакции с запросами на изменения самого агрегата. Другим потоком запрашивать необработанные события, отсортированные по autoincrement и применять их в Elasticsearch, затем ставить флаг, что событие было обработано. Такой вот Business Log в дополнение к состоянию.
Звучит как обычная очередь. Смотрите сразу на какой-нибудь RabbitMQ.
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: Переход от CRUD к CQRS/ES

Сообщение anton_z »

Большое спасибо за ответы.
zelenin писал(а): 2017.01.24, 10:15 смутно понял о чем речь. подкину лишь пару тезисов: а) id события можно генерить на клиенте в виде uuid, б) событиям можно сделать поле version, которое будет увеличиваться на клиенте, обеспечивать порядок событий и избавит от конкурентности.
version последовательна и уникальна для всех событий или в рамках агрегата?
samdark писал(а): 2017.01.24, 10:46 Звучит как обычная очередь. Смотрите сразу на какой-нибудь RabbitMQ.
Хочется консистентности между состоянием (сущностями) и событиями. Поэтому и очередь в таблице в той же БД на том же сервере под транзакциями. А так если кролик окажется недоступен в момент записи? Консистентность пострадает.
Еще AMQP не гарантирует порядок событий. А в этой задаче он важен.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Переход от CRUD к CQRS/ES

Сообщение zelenin »

anton_z писал(а): 2017.01.24, 11:22version последовательна и уникальна для всех событий или в рамках агрегата?
в рамках агрегата.
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: Переход от CRUD к CQRS/ES

Сообщение anton_z »

А, тянем последнее событие для агрегата с блокировкой, к его version + 1, получаем version для нового события.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Переход от CRUD к CQRS/ES

Сообщение zelenin »

просто ставим уникальный индекс на [entity_type, entity_id, version]. Блокировка - не самое лучшее решение.
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: Переход от CRUD к CQRS/ES

Сообщение anton_z »

Тут получается выбор Optimistic lock vs Pessimistic lock.

А при проигрывании событий я выбираю необработанные с сортировкой по entity_id, version, верно?
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Переход от CRUD к CQRS/ES

Сообщение zelenin »

anton_z писал(а): 2017.01.24, 14:09 Тут получается выбор Optimistic lock vs Pessimistic lock.
да
anton_z писал(а): 2017.01.24, 14:09А при проигрывании событий я выбираю необработанные с сортировкой по entit_id, version, верно?
да
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Переход от CRUD к CQRS/ES

Сообщение samdark »

Section 4.7 of the AMQP 0-9-1 core specification explains the conditions under which ordering is guaranteed: messages published in one channel, passing through one exchange and one queue and one outgoing channel will be received in the same order that they were sent. RabbitMQ offers stronger guarantees since release 2.7.0.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Переход от CRUD к CQRS/ES

Сообщение zelenin »

ну дело в том, что на одну очередь может быть открыто несколько каналов, и так и делается при хорошей загрузке. И если в пять каналов одновременно ушло пять задач с id от 1 до 5, то нет гарантии, что они выполнятся по порядку. Скорее есть гарантия, что наоборот.
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Переход от CRUD к CQRS/ES

Сообщение samdark »

Это и написано в цитате.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Переход от CRUD к CQRS/ES

Сообщение zelenin »

samdark писал(а): 2017.01.24, 16:02 Это и написано в цитате.
ну так anton_z об этом и говорит, ведь поддержка сортировки в одном канале очевидна, но зачастую не применима на практике.
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Переход от CRUD к CQRS/ES

Сообщение samdark »

А с базой разве будет больше одного канала?
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Переход от CRUD к CQRS/ES

Сообщение zelenin »

samdark писал(а): 2017.01.24, 18:14 А с базой разве будет больше одного канала?
а, ну да.
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: Переход от CRUD к CQRS/ES

Сообщение anton_z »

Признаю ошибку c AMQP.

Один черт не могу сторонние очереди использовать - не будет консистентности между очередью и базой. Это если сначала в очередь. потом подписчиком в базу, тогда да.

Про производительность - параллеллить можно на стороне обработчика событий по entity_id.
Ответить