Comments 51
Может есть смысл начать с отказа от веры в святой Грааль микросервисы?
Рекомендация: Для простых саг, где 2-3-4 шага и логика прямая как рельс, хореография может быть элегантным решением
Может в таком случае элегантным решением будет сервис с ACID?!
Я если честно вообще не понимаю как можно строить бизнес логику без единой базы данных на всю систему, консистентность данных для бизнеса в ИТ пространстве это жизненная необходимость.
Вы консистентность путаете с персистентностью. Обеспечению консистентности база только мешает.
Обеспечению консистентности база только мешает.
Таки помогает: она (если это не MySQL 20-летней давности) имеет в своем составе средство - поддержку транзакций - позволяющее гарантировать, что группа операций, сохраняющая консистентность только после завершения всей группы, но не каждой операции, будет или выполнена целиком, или не выполнена.
В отсутствии такого средства со стороны СУБД приложению приходится заботиться о сохранении согласованности самому приложению. И если вы успели поработать с десктопными/файл-серверными БД на основе файлов (формата dbf, Paradox и иже с ними), то должны были запомнить, какой геморрой это вызывало. Лично для меня Inetrbase с его транзакциями в Delphi стал буквально глотком свежего воздуха после этого геморроя.
Как насчет event sourcing? Люди стараются, работают, а оказывается, всё впустую.
Eventual consistency, упомянутая в тексте, под которым мы тут клавиатуры стираем — тоже пустой звук?
Меня больше интересует, чем база мешает?
Ну и исторический вопрос (стаж у вас вполне подходящий) - не приходилось ли с Клиппером или Парадоксом (ну, или с Фоксом, или даже с C с CodeBase) изворачиваться, чтобы свою, типа, транзакцию замутить: чтобы если с одного счета списалось, то на другой бы обязательно записалось, и наоборот? Если нет - завидую.
Шаблон источников событий - он, конечно, иногда решает проблему - когда событие удается записать одной записью. Но вот в бухучете типичную продажу товара с одновременной уплатой НДС (а в 90-е НДС уже был) так не проведешь: там нужны две проводки на разные счета. Ну и суммарные показатели при фиксации каждого события считаются медленно и печально.
Что до согласованности в конечном итоге, то согласованность в моменте все равно лучше, Только вот не всегда ее сделать можно: времена Клиппера возвращаются, так сказать, на новом витке спирали.
Но все-таки, чем база мешает-то?
① Меня больше интересует, чем база мешает?
GOTO ⑤
② не приходилось ли с Клиппером или Парадоксом (ну, или с Фоксом, или даже с C с CodeBase) изворачиваться, чтобы свою, типа, транзакцию замутить
Приходилось, конечно, но я даже тогда изобретал не транзакции, а event log.
③ Но вот в бухучете типичную продажу товара с одновременной уплатой НДС (а в 90-е НДС уже был) так не проведешь […]
Почему? Я в основном складские учёты тогда и писал, и не вижу никакой проблемы.
④ согласованность в моменте все равно лучше
Вообще ничем не лучше. Мне лично никогда не требовалась, более того: я вообще всё в последние несколько лет делаю асинхронно (с тех пор, как научился).
⑤ Но все-таки, чем база мешает-то?
Тем, что она рано, или поздно, превратится в узкое место. Для ларька в Мытищах — база лучшее решение, спору нет. Но если у вас каждый клиент может вдруг прислать 500 транзакций в секунду — уже нет, потому что надо очень тщательно обрабатывать таймауты, а это сложнее, чем сразу заложиться на eventual consistency.
Тем, что она рано, или поздно, превратится в узкое место.
Вы в предыущем неправильно выбрали форму глагола: правильная - "может когда-нибудь помешать".
Превратится-то оно когда-то, а база помогает сейчас. Или - вообще не превратится. К примеру, если ваш бизнес в типологии Белоусова из 90-х попадает в категорию "крысить" (большинство бизнесов из 90-х в нее попадали), этого не случится никогда. А современные СУБД - они весьма масштабируемые, по жизни на них работают очень крупные предприятия.
Ну, а что опыт у вас специфический - я понял. И вашу склонность делать чрезмерные обобщения, выходящие за пределы вашего опыта я тоже понял.
У меня очень разный опыт, на основании которого я могу сделать только два вывода (не без помощи Декарта и Мёрфи, конечно).
Я научился тянуть в проект базу только тогда, когда она действительно нужна, и только до той степени, в которой она помогает. С тех пор моя жизнь стала в разы проще и приятнее. Я не делаю обобщений: я просто стараюсь этот опыт заглядывания за пределы черты оседлости передать, хотя бы тем — кому интересно. Кому неинтересно — мои слова и так пустой звук, им и за джейсоноукладку нормально платят.
То есть, база, если её правильно приготовить, обеспечению консистентности не мешает, но базу часто готовят неправильно - я адекватно вас понял?
Если да - сравните с тем, что вы написали в исходном комментарии. И если вам нужжен совет, в следующий раз постарайтесь фрмулировать свои утвержения менее категорично.
Категоричные утверждения провоцируют дискуссию, иногда интересную и познавательную.
Почетное право мямлить набившие оскомину трюизмы — я без сожаления оставляю середнякам.
Есть много вещей которые в любом случае будут большей частью вне единой бд. Те же платежи - мастер-система будет вообще не в контуре инфраструктуры компании. Какая-нибудь система лояльности или tms - time management system.
Всегда интересовало, что будет, если шаг компенсации не может быть выполнен? Получается, к согласованному состоянию система не придёт никогда в этом случае
Тогда компенсирующие транзакции. Тоже геморрой тот еще. Либо использовать оркестратор вроде камунды (что само по себе весьма нетривиально), либо паттерны вроде transactional outbox, но по любому суета. Особенно если цепочка транзакций, приведших к такой ситуации длинная.
Будет кому-то развлечение на разруливание ситуации руками.
OrderService ловит InventoryUpdateFailed (или PaymentCancelled), ставит статус CANCELLED.
Если два сервиса ловят один и тот же ивент — это не сага. Можно ловить только PaymentCancelled в данном случае.
Если два сервиса ловят один и тот же ивент — это не сага.
Обоснуйте.
Можно ловить только PaymentCancelled в данном случае.
Вы накладываете излишнее ограничение на возможные сценарии. К примеру, если в заказе стоит "оплата курьеру", то PaymentService вообще не обязан принимать участие в обработке.
Вы не сможете правильно откатить всё назад, если два разных приёмника получили и применили (один успешно, второй нет) один и тот же ивент.
К примеру, если в заказе стои́т «оплата курьеру», то
PaymentServiceвообще не обязан принимать участие в обработке.
А если в заказе стои́т «взорвать бомбу» — то нужны еще сапёры. Я отвечал строго на приведенный в тексте пример.
Вы не сможете правильно откатить всё назад, если два разных приёмника получили и применили (один успешно, второй нет) один и тот же ивент.
Откат назад в саге с хореографией - личное дело каждого приёмника: он видит события отказа в саге и делает корректирующее действие, если раньше не сделал, по предыдущему событию.
Я отвечал строго на приведенный в тексте пример.
Да. А я написал про то, что будет, если его чуть-чуть расширить.
Ограничение в решении из примера - с чисто линейной обработкой одними и теми же участниками - слишком жесткое. Изменения в требованиях, скорее всего, заставят что-то с ним сделать.
Вариантов что-нибудь сделать, если не переходить к оркестрации, два - либо для каждой возможной цепочки заводить свою последовательность событий, и каждому микросервису выбирать, а какое из событий ему публиковать - и где-нибудь когда-нибудь что-нибудь обязательно будет опубликовано неправильно, либо - отказываться от линейной обработки и делать обработку по событиям - это куда интереснее,путь обработки становится, вообще говоря, непредсказуемым, разборы трассировок скучать не дадут, но если пройти этим путем недалеко, подправив в 1-2 местах, то выжить можно, а кода править, скорее всего, в этом варианте придется меньше. А вы своим заявлением этот путь закрываете.
В любом случае, если захочется идти дальше в хореографию любым из этих путей - то читать эту статью и проникнуться важностью оркестрации. Можно не читать, кончено, и проникаться важностью на своей шкуре при разборке инцидентов - но это уже на выбор руководителя проекта, а автор статьи предупредил.
Я использую саги с хореографией для крайне запутанных систем уже примерно восемь лет. И всё отлично (возможно пока, и теперь, когда я невнимательно прочитал текст в интернете — всё поломается).
Откат назад в саге с хореографией — личное дело каждого приёмника […]
Угу. Именно поэтому и нельзя реагировать на один и тот же ивент из разных мест.
Хореография куда ни шло, когда в цепочке пара-тройка транзакций. А по мере роста задействованных сервисов, мне кацца, начнутся траблы. Ведь каждый сервис, участвующий в формировании итогового состояния, должен знать что случилось (по меньшей мере) на предыдущем шаге, за который отвечает другой сервис. Оркестрация хоть и сложнее (требует оркестратора), но тут хотя бы вся информация в одном месте, а не размазана по всему ландшафту.
И, к слову, никто не отменяет того факта, что и оркестратор тоже может лечь и на какое-то время быть не вполне работоспособным. Да, я знаю, что должно быть несколько узлов и так далее. Но все это (что хореография, что оркестрация) резко повышает стоимость поддержки, развертывания да и просто тупо предполагает ощутимые затраты на железо, маршрутизацию и проч.
Я не против микросервисов - это красиво и по своему элегантно. Но надо понимать, чего эта красота стоит. Для мега проектов вроде amazon/netflix по другому, наверное, и нельзя: будет больно и сложно. Тут эффект масштаба бизнеса (плюс эффект мега бабла) работает на микросервисы. Но в куче проектов микросервисы это понты. Поначалу - прикольно. Даже MVP выглядят ничего себе. Но когда дело доходит до прода, то начинаются танцы с бубном.
Именно поэтому и нельзя реагировать на один и тот же ивент из разных мест.
Ну почему же? Если компенсирующее действие по факту идемпотентно - например, в нем фиксируется, что оно уже было применено, и повторное применение ничего не делает - то почему бы его не приенить несколько раз?
Но, конечно, компенсирующее действие по жизни может внезапно оказаться фактически не идемпотентным (ну, к примеру, из-за гонки при его параллельном применении) - и тогда всё будет интереснее, да. Так что смысл накладывать ваше ограничение есть. Но смысл его не накладывать есть тоже - я как про это и писал. Диалектика, однако.
Да дело даже не в идемпотентности: ветвления в общем случае откатить в триста раз сложнее, спросите любого, кто хоть раз в жизни реализывывал Undo/Redo. Возвращаясь к вашему оригинальному примеру:
Вы накладываете излишнее ограничение на возможные сценарии. К примеру, если в заказе стоит «оплата курьеру», то
PaymentServiceвообще не обязан принимать участие в обработке.
PaymentService по-прежнему принимает участие в обработке, тут даже напрямую: у него состояние изменяется на «оплата курьеру», но даже если нет — это проще всего решается пустым безусловным переходом в стейт-машине.
PaymentService по-прежнему принимает участие в обработке, тут даже напрямую: у него состояние изменяется на «оплата курьеру»,
Я предполагал другое разделение ответственности: PaymentService занимается чисто приемом платежей. Он сервис простой: попросили оплатить - организовал оплату. И даже состояние счета клиента - не его дело.
А в целом, обсуждать ваш вброс (это же ведь был вброс, правда?) дальше не вижу смысла: теоретических вариантов возможно много, а практическое решение, где надо учитывать конкретику (для каждого решения свою), мы тут не делаем.
Да дело даже не в идемпотентности: ветвления в общем случае откатить в триста раз сложнее, спросите любого, кто хоть раз в жизни реализывывал Undo/Redo.
Я реализовывал, но, кмк, перемудрил, записывая коды действий. Кмк, достаточно было просто записывать снимки системы. И не беспокоиться по-поводу памяти.
По-поводу идемпотентности — в параллельных системах, насколько я понимаю, это важно. Вернее важно представлять себе пути к цели вычислений как точки на решётке (в математическом смысле), тогда можно частично дублировать вычисления на разных нодах, а потом сливать их вместе. При этом естественным образом возникает идемпотентность в конечной точке.
Это я так криво рассказываю формализм за Lvars — https://www.janestreet.com/tech-talks/abstractions-for-expressive-efficient-parallel-and-distributed-computing/ (как у них это заведено, на Hackage протухло и авторы забили).
Снимки там, или коды — это детали реализации. Что действительно важно, это то, как принимается решение «куда теперь» после такой последовательности:
A → B → [UNDO] A → C → [UNDO] A → REDO???
Композиция двух идемпотентных действий не обязательно идемпотентна, но да, при прочих равных лучше вообще всё и всегда стараться делать идемпотентным, покуда возможно.
Торадиционно откатывается только верхнее действие. А после действия стирается буфер Redo.
А если действие было тождественно Redo?
Как написать плохой механизм, я знаю :)
А если действие было тождественно Redo?
А то же самое — это GUIшный редактор схем, там вероятность, что так произойдёт почти нулевая. И результат будет сюрпризом для всех.
А вы для какой ситуации писали? «Доктор, мне бы очень хотелось поговорить об этом» — тема меня реально интересует.
Я уж было, как все, собрался писать про токсичную атмосферу, лестницы и потолки, да суррогатный интеллект, но спасибо, вы подарили идею своим вопросом.
Допишу вот немного кода за еду, и возьмусь, ждите.
Допишу вот немного кода за еду, и возьмусь, ждите.
Спасибо. А вкусно хоть кормят? И питательно ли?
Потолки, конечно, надо белить, но это более-менее бессмысленный трёп. Тут все всё понимают - инженеры, всё-таки. Но цели в жизни разные. У меня большая часть минусов в карме "политика/пропаганда", подозреваю, что и большая часть плюсов ровно за то же самое.
Как по мне то CDC паттерн по интересней будет
Судя по Хабру каждый первый в компаниях типа Гугла работает и у них без кучи микросервисов ничего работать не может. Скорее всего у 90 процентов здешних посетителей все данные спокойно в ОЗУ нормального сервера влезут…. Соответственно проблема есть но нашего уровня. Если поднапрячься то все можно спокойно впихнуть в классическую БД и наслаждаться всей прелестью встроенного транзакционного движка.
Для любителей накрутить накрутить сан и тому подобное: если вы в Oracle запустили транзакцию, потом поехали в отпуск на месяц-другой, потом вернулись и продолжили выполнение запроса в той же транзакции, то вам гарантируют что данные вы увидите в состоянии в котором они были месяц назад.
Зачем вам это надо- другой вопрос. .. как вы это на сагах реализовать собираетесь?
А компенсирующая транзакция же тоже может сбойнуть, значит на нее надо компенсирующую транзакцию следующего уровня:-)
У меня все чаще складывается впечатление, что программисты уже совсем запутались в поисках серебрянной пули и погоне за хайпом: "Подход Х мертв, да здравствует Y", "Вы не поняли X", "Почему X недооценен", "Почему Y переоценен", "X против Y, что лучше в 2025?". Бессмыслица.
На самом деле, процесс моделирования достаточно прозрачен. Сначала необходимо определить, какие данные нужно обработать и какие состояния они могут принимать. В результате мы получаем машину состояний. Затем выделяем агрегаты, которые определяют границы консистентности данных. Консистетный переход между состояниями может происходить только в рамках одного агрегата. Например, в машине состояний, отвечающей за перевод средств между счетами, агрегатами могут быть "счет" и "перевод". Консистентные переходы для агрегата "перевод" могут включать: перевод запрошен, деньги списаны с отправителя, деньги отправлены получателю, перевод завершен. Переходы между агрегатами являются неконсистентными. Например, если деньги уже списаны со счета отправителя, но статус перевода все еще "перевод запрошен", это является неконсистентным переходом. Границы консистентности определяются в зависимости от конкретного юзкейса: чем менее консистентна система, тем сложнее становится машина состояний (больше промежуточных состояний), но повышается доступность; наоборот, чем более консистентна система, тем проще машина состояний, но снижается доступность.
Теоретически возможно спроектировать всю систему как единый агрегат "банк". Это обеспечит высокую консистентность, но приведет к значительным задержкам, и при увеличении числа пользователей система станет практически непригодной для работы, поскольку агрегат не сможет поместиться в оперативной памяти, а любая операция будет занимать минуты только на IO.
Далее, рассмотрим оркестрацию и хореографию. Как правило, одному процессу соответствует одна машина состояний (например, перевод денег). В процессе работы одной машины состояний может быть активирована другая машина состояний с собственным жизненным циклом (например, проверка подозрительной активности). Когда мы говорим об оркестрации и хореографии, мы имеем в виду не выбор одного из этих направлений, а целый спектр возможностей. Можно реализовать централизованную оркестрацию, выделить службу оркестрации для определенной категории процессов, службу для конкретного процесса или распределить логику оркестрации между различными службами по принципу "поближе к тем данным, которые обслуживаются сервисом". Чем ближе к централизованной оркестрации, тем проще система, но она может оказаться несбалансированной и менее устойчивой при масштабировании. С другой стороны, чем больше децентрализация, тем сложнее становится управление и координация, но повышается устойчивость отдельных частей системы.
Настоящее мастерство программиста заключается не в том, чтобы соревноваться в знании паттернов, а в способности проанализировать задачу в контексте бизнеса и системы, а затем совместно с бизнесом выбрать наиболее подходящее решение для конкретного случая.
Знаете, проблема саг в том, что вот это
...получаем итоговую согласованность (Eventual Consistency). То есть система придет в согласованное состояние...
это не правда. Потому что те данные, которые уже обновлены в рамках саги, могут быть прочитаны другими системами, и тогда никакая (НИКАКАЯ!) самая хитрая компенсация и игры в soft delete и версионирование моделей вас не спасут. По сути сага - это транзакция с изоляцией read uncommited, со всеми прелестями.
Именно поэтому, например, на моем месте работы (российский бигтех, который на конфах очень любил рассказывать, как у нас активно используются саги) от этих самых саг практически везде отказались. Потому что никаких проблем они по сути не решают, а на деле только добавляют.
По опыту, единственный реально работающий вариант на распределенных системах - это event driven, без попыток в синхронщину.
Именно поэтому, например, на моем месте работы (российский бигтех, который на конфах очень любил рассказывать, как у нас активно используются саги) от этих самых саг практически везде отказались.
А продолжить сагу дальше на потребляющие сервисы у вас там не пробовали? С хранением старой локальной копии и новой до момента подтверждения считающейся недостоверной? Правда, я тут, кажется, переизобрел двухфазную фиксацию? ;-)
По опыту, единственный реально работающий вариант на распределенных системах - это event driven, без попыток в синхронщину.
А что, сага - это обязательно синхронно? Для меня это была бы новость.
И да, сага, как я понимаю - это не про изоляцию транзакций по чтению, а всего лишь способ сохранить согласованность данных в наборе хранилищ микросервисов в конечном итоге. Там где по требованиям задачи требуется читать только те данные, которые будут устойчивы в конечном итоге (аналог Read Committed), скорее всего придется извернуться и таки эмулировать транзакцию с изоляцией этого уровня. Но, наверное, не все задачи этого требуют?
Правда, я тут, кажется, переизобрел двухфазную фиксацию?
именно. У вас все те же проблемы (упали на n-ом сервисе) просто вылезут на фиксации
И да, сага, как я понимаю - это не про изоляцию транзакций по чтению, а всего лишь способ сохранить согласованность данных в наборе хранилищ микросервисов в конечном итоге.
еще раз - с учетом доступности для чтения данных из незаконченой саги - нет там согласованности. Причем при откате данных для потребителей увиденные сырые данные будут "галлюцинацией"
Там где по требованиям задачи требуется читать только те данные, которые будут устойчивы в конечном итоге (аналог Read Committed), скорее всего придется извернуться и таки эмулировать транзакцию с изоляцией этого уровня. Но, наверное, не все задачи этого требуют?
я же написал про event driven - только он и спасает. Просто примите в своем сердце, что во вселенной нет и не может быть неразрывных зависимых событий. И каждое промежуточное состояние системы рассматривайте как консистентное - грубо говоря нарисуйте в голове стейт машину, где каждый переход выполняется строго в одном сервисе (и хранилище). Условно не обязательно сразу и создавать заказ и списывать бабки. Заказ создали, товары захолдировали на 15 минут, кинули событие - другая система прочитала, попыталась списать бабки, не смогла, кинула событие - первая система прочитала событие о том, что не прошла оплата, отменила заказ и расхолдировала товары.
На самом деле любая "транзакция" без проблем раскладывается на такой граф атомарных переходов и натягивается на событийку
По сути сага — это транзакция с изоляцией read uncommited, со всеми прелестями.
Нет конечно. По сути сага — это [распределенный] конечный автомат. В том, что вы пытались использовать ее как транзакцию — виновата не сага, а ваше неверное понимание того, как ее готовить. Это сродни заявлению: «все равно в промежуточных состояниях конечное состояние не достигнуто, поэтому стейтмашины не работают». При чтении данных надо понимать, что в рамках саги (как любого конечного автомата) — данные это не data, а кортеж {data, state}. Вы же не жалуетесь, что созданный, но пока не оплаченный заказ, — это не консистентно? — Так же и тут.
единственный реально работающий вариант на распределенных системах — это event driven, без попыток в синхронщину
А вот это — абсолютно верно, хоть в граните отливай.
Но отсюда никак не следует, что саги не работают. В некоторых случаях — вполне себе, причем прям поверх event driven, прикиньте :)
В том, что вы пытались использовать ее как транзакцию — виновата не сага, а ваше неверное понимание того, как ее готовить. Это сродни заявлению: «все равно в промежуточных состояниях конечное состояние не достигнуто, поэтому стейтмашины не работают». При чтении данных надо понимать, что в рамках саги (как любого конечного автомата) — данные это не
data, а кортеж{data, state}. Вы же не жалуетесь, что созданный, но пока не оплаченный заказ, — это не консистентно? — Так же и тут.
Простите, но нет.
Первое: сама по себе концепция саг - это откатываемость действий. Без откатываемости - это не саги. И да, саги так и используются - как распределенное транзакции. Это в общем единственное, зачем они нужны.
Второе: обычно в реализации саг нет никакого state (флага закоммичернности данных), а даже если вы его туда затащите - вы по сути сделаете двухфазные коммиты и это вас не спасет, так как вы можете отвалиться на третьем запросе коммита (в двух предыдущих будут уже данные со стейтом "закоммичено") и получите все те же проблемы.
Третье: проблема саг в самой концептуальной возможности откатиться. Откат - это не переход по стейт машине, это как раз откат перехода. Именно ради возможности откатиться саги и используют, вместо нормальной обработки каких-то проблем с переходом "вперед" (простите за сумбур, но думаю мысль понятна).
Четвертое: при event-driven подразумевается не откатываемость, а штатная обработка проблем с движением "вперед" по стейт-машине. На кой черт в event-driven примешивать саги - мне решительно не понятно.
Если четно у меня подозрение что вы под сагами подразумеваете не совсем саги, а как раз event-driven с движением вперед по стейт-машине. Этот подход я целиком поддерживаю, но это не саги.
Грубо говоря "захолдировали деньги (переместили в отдельный временный карманчик) -> попытались забронить товары -> бронь не прошла -> снимаем холд с денег (вернули из карманчика на счет)" - это не сага, это нормальный event-driven с движением "вперед" по стейт-машине.
А вот "реально списали бабки со счета в базе -> обломались на бронировании товара -> откатили списание в базе" - вот это уже сага
Давайте принесём в этот разговор аксиоматику, для начала, а то скоро начнутся аргументы «моя сага длиннее».
Вот тут впервые было дано определение, поэтому нет никаких причин его не использовать (тем более, что все современные ссылки дают его же):
A Long Lived Transaction (LLT) is a saga if it can be written as a sequence of transactions that can be interleaved with other transactions. The database management system guarantees that either all the transactions in a saga are successfully completed or compensating transactions are run to amend a partial execution.
Вместо «database management system» в общем случае нужно, конечно, читать «saga management system», это не обязательно база, но чуваки старались генерализовать по-минимуму.
Итак. Тут прямым текстом сказано, что существование компенсирующих транзакций — обязательное условие. Если нельзя определить компенсируюшую транзакцию на всем множестве значений — саге не бывать. Поэтому:
реально списали бабки со счета в базе → обломались на бронировании товара → откатили списание в базе
— это действительно сага, потому что компенсирующая транзакция определена всегда. А вот:
перевели деньги Иванову на счет → Иванов закупился на всё лотерейными билетами → откатили изначальный перевод
— уже нет. Понятно, потому что реверсивная транзакция для перевода не определена в данном случае. Зато:
перевели деньги Иванову на счет → Иванов закупился на всё лотерейными билетами → откатили закупку лотерейных билетов → откатили изначальный перевод
— снова да.
В общем,
Откат — это не переход по стейт машине, это как раз откат перехода.
Это реверсивная транзакция, если она определена — все в порядке. В этой стейт машине должен быть определен переход назад (я зря не сказал, конечно, что это не любой конечный автомат в вакууме, а такой специальный, у которого всем исходящим стрелочкам соответствуют парные входящие).
На кой черт в event-driven примешивать саги — мне решительно не понятно.
Опять же, для наведения порядка, для поверки, так сказать, алгеброй гармонии ивентдрайвинга (-драйва). Если есть валидированная сага, значит можно вызвать «undo» и не париться, а не городить специальную функцию обработки «вот такой ивент вот в такой ситуации». Хоть на аспектах этот «undo» впилить — и voilà.
Во-первых, мне понравилось, что вы начали с того что "сага - это не распределенная транзакция", а потом принесли источник, в котором четко в первом абзаце написано, что сага это "Long Lived Transaction" с определенными условиями )).
Ну а во вторых, я вас кажется понял - по-вашему любая стейт машина, в которой можно вернуться в предыдущее состояние (даже через кучу переходов) - это уже сага. Мысль понятна, но нет, это не сага. Сага это не концепция, а просто шаблон проектирования, когда "надо выполнить n запросов или все или ни одного, если упадем, на выполненные вызываем компесационные запросы". Даже по вашей ссылке это там ниже нормально расписано, почитайте.
я вас кажется понял - по-вашему любая стейт машина, в которой можно вернуться в предыдущее состояние (даже через кучу переходов) - это уже сага
Нет.
Я настаиваю на «определенных условиях». А именно, на доказанном существовании компенсационного запроса. В обычной транзакции такого требования нет: заморозили мир, попробовали ряд последовательных действий, получилось — ура, нет — откатились. Никаких дополнительных ограничений.
У саги эти ограничения есть, и они крайне важны (но про них редко вспоминают, даже в документации разного рода). И если для каждой потенциальной саги проверять (доказывать) наличие компенсационных запросов, примениых на всем множестве возможных данных — с сагами проблем не будет.
https://learn.microsoft.com/en-us/azure/architecture/patterns/saga
Я уже года два нахожусь в восторге от этой великолепной статьи на сайте аж цельного майкрософта, которая как будто написана студентом первокурсником, которому надо было отвечать на билет про саги, а он не готовился и поэтому вспомнил обрывками все, что слышал про распределенные системы.
Зато это объясняет, откуда эта каша в дискуссии, когда "саги можно и с event-driven" и отсутствие нормального определения - что же такое собственно сага (а это просто паттерн проектирования, когда "выполняем n запросов, если один отвалится - выполняем компенсирующие запросы" - в прямом смысле компенсирующие, которые изначально сделаны как компенсирующие и их наличие/отсутствие не надо доказывать, доказывать - это про переходы по графу/стейт-машине)
И те ограничения, про которые вы говорите - это не часть шаблона, это как раз результат печального опыта работы с этим шаблоном, наработанный потом и кровью))

Микросервисы и данные: Как Saga-паттерн спасает от хаоса транзакций