S>>>МСА предлагает использование Representational State Transfer. Работает он так: S>>>1. Заказ в базе Б находится в статусе "черновик". S>>>2. По команде "зарезервировать заказ" заказ атомарно переходит в статус "резервируется". Из этого статуса его вручную вывести нельзя, и изменить состав заказа в этом статусе тоже нельзя. S>>>3. Сервисом заказов делается попытка выполнить идемпотентную операцию "создать резерв" в сервисе А. S>>>3.1. Если сервис А отвечает отказом, то сервис Б атомарно возвращает заказ в статус "черновик" и пишет в историю заказа "резервирование не удалось". S>>>3.2. Если сервис А отвечает подтверждением, то сервис Б атомарно перемещает заказ в статус "зарезервировано", и для него становятся доступны следующие операции стейт-машины. S>>>3.3. Если сервису Б не удается обработать ответ от сервиса А по любой из причин, перечисленных в моём предыдущем посте, то заказ остаётся в статусе "резервируется". G>>И что будет если приложение упадет в пункте 3.2 между ответом А и изменением статуса заказа в Б? S>Тут нам придётся немножко погадать о том, что такое "приложение" в этом сценарии.
У нас мобильное приложение или браузер, обращается через REST API к серверу, а сервер в свою очередь обращается к БД. Типичная трехзвенная архитектура.
S>Ну, допустим, это тонкий стейтлесс-клиент, всё "падение" которого — это браузер, который внезапно устал ждать разрешения пользователя на установку апдейта и самопроизвольно перезапустился.
Все проще, пользователь просто закрыл страницу когда не дождался ответа или интернет ему выключили. В любом случае сервер увидел потерю соединения и запустил отмену через CancellationToken, который из контроллера прокидывается во всю БЛ.
И это событие по счастливой случайности происходит ровно в момент между тем как А ответил подтверждением, а на сервер Б еще не отправлена команда на изменение статуса заказа.
Даже если мы эту отправку на серверы А и Б делаем на сервере, а не клиенте, то проблема никуда не девается. Процесс на сервере тоже также может упасть между.
S>Пользователь видит ту же страницу того же заказа, статус которого приложение перезапрашивает у Б через API и видит соответствующий статус "товары в резерве, произведите оплату до ЧЧ:ММ ДД.ММ.ГГГГ".
В описанном выше сценарии пользователь перезагрузивший страницу\приложение увидит что заказ все еще в статусе "резервируется".
G>>Я в принципе знаю правильный ответ: он называется WAL, а на более высоком уровне абстракции — transactional outbox. Данные для выполнения транзакции сначала атомарно пишутся в (одно, иначе атомарности не будет) долговременное хранилище, а потом пытаются примениться к конкретным таблицам. S>Да, в некоторых случаях мы можем попытаться нарулить что-то вроде WAL. Но не всегда, т.к. под капотом у WAL — элементарные таблицы, для которых чётко и однозначно определены операции чтения и записи.
Что именно будет сохранено в WAL зависит в итоге от реализации хранилища, к которому мы будем этот самый WAL применять.
S>Это и даёт нам железобетонную уверенность в том, что после рестарта мы можем откатить неоконченные транзакции и накатить оконченные. S>Когда мы проектируем в терминах сервисов, такой гарантии у нас в общем случае нет.
Вот мы и выяснили, что в БД есть WAL, что дает нам гарантию, а в МСА для гарантии надо свой WAL изобрести, и то не факт что получится, ибо там другие хранилища.
G>>>>Если код падает по между шагами 2 и 3, то в базе остается несогласованное состояние. S>>>Несогласованное состояние в REST возникает только в тот момент, когда мы не получили ответ на запрос "распространить изменение". Причём мы знаем о том, что оно несогласованное — и можем принять меры, чтобы эта несогласованность не вышла нам боком. G>>Несогласованное состояние в примере выше возникнет если заказ на складе будет забронирован в сервисе А, а на сервисе Б не изменен статус. S>Вы говорите то же самое, что и я, немного другими словами. Потому, что "в сервисе Б не изменён статус" == "сервис Б не смог получить ответ на запрос о распространении изменения". S>Как только он сможет получить этот ответ, статус заказа сразу же и изменится. Без каких-либо рукопашных действий со стороны пользователя или администратора.
От кого он этот ответ должен получить? Какой процесс должен сработать?
Мне кажется дальнейшее пока рано обсуждать, потому что мы пока не достигли единого понимания сценария фейла для "транзакции" из двух таблиц. Когда поймем эти сценарии и поймем как их можно компенсировать за счет кода можно обсуждать все остальное.