Как корректно внести изменения в несколько агрегатов за раз?

Обсуждаем, как правильно строить приложения
Ответить
glagola
Сообщения: 47
Зарегистрирован: 2017.02.22, 19:43

Как корректно внести изменения в несколько агрегатов за раз?

Сообщение glagola »

Всем привет!

Столкнулся с проблемой как внести изменения в несколько агрегатов за один запрос (знаю что это крайне не рекомендуется, но придумать как это обойти пока не могу).

Суть
  • Есть агрегат объявление пользователя, к которому могут прикрепляться/загружаться фотографии (сейчас это реализованно как коллекция идентификаторов изображений).
  • Есть агрегат изображение. При загрузке нового изображения, оно помечается как временное и если никто не изменит его статус на постоянное, оно будет удалено через определенное время.
Сценарий №1
После создания нового объявления, нужно все изображения, которые были загружены пользователем, пометить как постоянные.

Сценарий №2
После редактирования объявления, нужно все удаленные изображения пометить как временные (чтобы сборщик мусора их вычистил), а новые пометить как постоянные.

Как реализовано сейчас
На данный момент оба сценария я реализовал через доменные события, в обработчиках данных событий я обращаюсь к application сервисам (persist и expire), чтобы пометить картинки как временные/постоянные. Все происходит в той же транзакции, что и создание/изменение самого пользовательского объявления.

Вопросы
  • На сколько текущая реализаци корректна?
  • Может стоить выделить изменение статусов картинок в отдельную транзакцию? а в будущем уже вынести это действие в воркеры
  • Насколько корректно изменять статус всех картинок (те что были удалены/добавлены) в одной транзакции? т.е. изменять много агрегатов одного типа в рамках одной транзакции
Заранее спасибо!

P.S. Любые ваши идеи приветствуются :)
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: Как корректно внести изменения в несколько агрегатов за раз?

Сообщение anton_z »

У вас один агрегат - обхявление.
Файлы изображений где хранятся? в БД?
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Как корректно внести изменения в несколько агрегатов за раз?

Сообщение samdark »

По идее, всё это должно быть одним агрегатом.
glagola
Сообщения: 47
Зарегистрирован: 2017.02.22, 19:43

Re: Как корректно внести изменения в несколько агрегатов за раз?

Сообщение glagola »

anton_z писал(а): 2017.04.04, 13:53 У вас один агрегат - обхявление.
samdark писал(а): 2017.04.04, 14:06 По идее, всё это должно быть одним агрегатом.
В проекте 4 вида объявлений + аватарки пользователей. Поэтому выделил изображения в отдельную подсистему, которая отвечает за загрузку, хранение, кроп&ресайз(+валидация размеров кропнутого изображения), удаление. В последствии nginx кэширует ответ с кропнутым изображением.
anton_z писал(а): 2017.04.04, 13:53 Файлы изображений где хранятся? в БД?
Да в БД, очищенные оригиналы изображений лежат в БД + мета информация (текст для атрибутов alt, размеры).
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Как корректно внести изменения в несколько агрегатов за раз?

Сообщение zelenin »

glagola писал(а): 2017.04.04, 13:24 Как реализовано сейчас
На данный момент оба сценария я реализовал через доменные события, в обработчиках данных событий я обращаюсь к application сервисам (persist и expire), чтобы пометить картинки как временные/постоянные. Все происходит в той же транзакции, что и создание/изменение самого пользовательского объявления.
ну ок, хорошо реализовано - через события. Только в разных транзакциях надо обрабатывать.

1. создали запись, сохранили в сервисе/репозитории в рамках транзакции, сгенерировали событие PostCreated, хэндлер хэндлит событие, помечая картинки временными в рамках второй транзакции.
2. апдейтнули пост, добавив новых картинок, сохранили в сервисе/репозитории в рамках транзакции, сгенерировали события ImageDeleted (или что там у вас), хэндлер захэндлил в рамках других транзакций.
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: Как корректно внести изменения в несколько агрегатов за раз?

Сообщение anton_z »

glagola писал(а): 2017.04.04, 15:41 Да в БД, очищенные оригиналы изображений лежат в БД + мета информация (текст для атрибутов alt, размеры).
То есть у вас есть коллекция идентификаторов изображений в объявлении? Сначала грузятся и сохраняются временные файлы, потом в первой транзакции в объявление записываются идентификаторы, потом отдельный процесс обновляет картинки?
Если так, то все круто.
glagola
Сообщения: 47
Зарегистрирован: 2017.02.22, 19:43

Re: Как корректно внести изменения в несколько агрегатов за раз?

Сообщение glagola »

anton_z писал(а): 2017.04.04, 16:23 То есть у вас есть коллекция идентификаторов изображений в объявлении?
Да, только идентификаторы.
anton_z писал(а): 2017.04.04, 16:23 Сначала грузятся и сохраняются временные файлы, потом в первой транзакции в объявление записываются идентификаторы, потом отдельный процесс обновляет картинки?
Да, все так.
glagola
Сообщения: 47
Зарегистрирован: 2017.02.22, 19:43

Re: Как корректно внести изменения в несколько агрегатов за раз?

Сообщение glagola »

zelenin писал(а): 2017.04.04, 15:56 Только в разных транзакциях надо обрабатывать.
Правильно ли я понимаю, что статус каждого изображения (новое/удаленное) должен быть изменен в отдельной транзакции? или можно все удаленные изображения пометить как expired в рамках одной транзакции?
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: Как корректно внести изменения в несколько агрегатов за раз?

Сообщение anton_z »

Подождите, у вас события персистентны? Если нет, при таком подходе можт возникнуть неконсистентность между коллекцией идентификаторов и изображениями. Консистентность должна быть обязательно.
Тут либо событие в одной транзакции с объявлением сохранять, либо изображения обновлять в той же транзакции. Консистентность должна быть в приоритете.
glagola
Сообщения: 47
Зарегистрирован: 2017.02.22, 19:43

Re: Как корректно внести изменения в несколько агрегатов за раз?

Сообщение glagola »

anton_z писал(а): 2017.04.05, 00:34 Подождите, у вас события персистентны? Если нет, при таком подходе можт возникнуть неконсистентность между коллекцией идентификаторов и изображениями. Консистентность должна быть обязательно.
Нет, события не персистентны.
anton_z писал(а): 2017.04.05, 00:34 Тут либо событие в одной транзакции с объявлением сохранять, либо изображения обновлять в той же транзакции.
Да тут вы правы, надеюсь в будущем придем к Event sourcing, а пока буду все делать в одной транзакции. Благо пока переключаться между одной/многими транзакциями легко.
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: Как корректно внести изменения в несколько агрегатов за раз?

Сообщение anton_z »

Событие можете записать в БД в первой транзакции. Потом в каком-нибуль воркере его обработать - выставить изображениям нужный статус, удалить/пометить событие как обработанное. Будет eventual consistency.
glagola
Сообщения: 47
Зарегистрирован: 2017.02.22, 19:43

Re: Как корректно внести изменения в несколько агрегатов за раз?

Сообщение glagola »

anton_z писал(а): 2017.04.05, 14:01 Событие можете записать в БД в первой транзакции. Потом в каком-нибуль воркере его обработать - выставить изображениям нужный статус, удалить/пометить событие как обработанное.
Не уверен, что так это будет работать. Дело в том что сейчас связь односторонняя, если по какой-то причине воркер не сможет выполнить свою задачу (пометить изображение как постоянное) и спустя какое-то время данное изображение будет удалено сборщиком мусора, то объявление по прежнему будет ссылаться на несуществующее изображение.
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: Как корректно внести изменения в несколько агрегатов за раз?

Сообщение anton_z »

Ну там временное окно должно быть. И воркер должен пытаться несколько раз. По доменному транзитивному событию у вас ситуация еще хуже: транзакция на объявление закоммитилась, а на картинки зафейлилась. И тишина... Событие испарилось вместе с процессом интерпретатора и никто уже не поможет картинкам избежать неминуемого удаления) А если событие персистентно - воркер может сделать несколько попыток обработать его. Это и есть "консистентность в конечном счете". Если хотите все и сразу - картинки надо подводить под основную транзакцию на объявление.
glagola
Сообщения: 47
Зарегистрирован: 2017.02.22, 19:43

Re: Как корректно внести изменения в несколько агрегатов за раз?

Сообщение glagola »

anton_z писал(а): 2017.04.05, 16:35 Ну там временное окно должно быть.
Да, окно есть - 1 час.
anton_z писал(а): 2017.04.05, 16:35 И воркер должен пытаться несколько раз.
Даже если он будет пытаться несколько раз, то не факт что он преуспеет. В этом случае и кроется загвоздка. Я понимаю, что данный случай крайне редок, но чем больше пользователей тем больше вероятность происхождения самого невероятного сценария. Вижу пару варантов решения:
  • Превратить одностороннюю связь в друстороннюю - вместе с событием изменения статуса изображения, сериализовать и fallback событие, которое будет эмитировано воркером после критического кол-ва неудач.
  • Делать переодическую сверку БД на консистентность: отвязывать от объявлений изображения, которые были удалены из-за критического кол-ва неудач воркера изменить статус изображения.
anton_z писал(а): 2017.04.05, 16:35 Если хотите все и сразу - картинки надо подводить под основную транзакцию на объявление.
Оставил так как вы и говорите, пока не будет найдено приемлемое решение.
sda
Сообщения: 334
Зарегистрирован: 2013.12.19, 09:29

Re: Как корректно внести изменения в несколько агрегатов за раз?

Сообщение sda »

anton_z, а воркер работает с данными или доменными объектами?
sda
Сообщения: 334
Зарегистрирован: 2013.12.19, 09:29

Re: Как корректно внести изменения в несколько агрегатов за раз?

Сообщение sda »

Здесь используют доменные объекты и это логично https://github.com/florindpreda/Eventua ... onsumer.cs

но я не пойму как правильно действовать если на доменное событие необходимо изменить несколько разных доменных объектов ? Получается, что на доменное событие можно изменить только один тип доменного объекта, который в свою очередь кинет следующее доменное событие, по которому изменится следующий доменный объект и так далее по цепочке. Верно ?
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: Как корректно внести изменения в несколько агрегатов за раз?

Сообщение anton_z »

Можно с доменными, можно просто SQL-запрос, если никакие события не нужны. Не усложняйте, если от этого нет профита никакого.
sda
Сообщения: 334
Зарегистрирован: 2013.12.19, 09:29

Re: Как корректно внести изменения в несколько агрегатов за раз?

Сообщение sda »

А в чем профит обычного sql запроса? Ведь в таком случае, чтобы обновить несколько агрегатов нужно будет раздуть транзакцию кучей sql запросов. Чем жирнее транзакция тем больше шансов, что она зафейлится. Это уже не eventual consistency будет, ведь он как раз об обратном.
Ответить