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

Обвязка, которая это лечит, состоит из четырёх элементов: ретраи с растущей задержкой, идемпотентный ключ, очередь необработанного с ежедневной сверкой и алерт конкретному человеку. Собирается она за 16 часов работы инженера, стоит 48 000 ₽ и окупается на первом же длинном отказе внешней системы.

Но сначала — про то, что делает обвязку осмысленной. Сбои бывают четырёх типов, и правильная реакция у них разная. Половину типов ретраить нельзя вообще: повторная попытка даст тот же результат, зато создаст дубли.

Четыре типа сбоев и разная реакция на каждый

Сценарий, где на все ошибки стоит одна и та же реакция «повторить три раза», хуже сценария без обработки ошибок вообще: он ещё и портит данные. Разберём типы по порядку.

Тип сбояКак выглядитПравильная реакцияЧего делать нельзя
НедоступностьТаймаут, обрыв соединения, ответы 502 и 503, «сервис на обслуживании»Ретрай с растущей задержкой, 3 попытки, затем в очередь необработанногоПовторять без задержки в цикле: сценарий уходит в бесконечный прогон и добивает диск журналами
Превышение лимитаОтвет 429, «слишком много запросов», исчерпана суточная квотаПауза на время, указанное сервисом, затем снижение частоты обращенийПродолжать долбить: многие сервисы продлевают окно блокировки при попытках во время бана
Невалидные данныеНет обязательного поля, телефон в свободной форме, отрицательная сумма, дубль по ключуНе ретраить. Сразу в очередь на разбор человеком, с сохранением исходной записи целикомРетраить: результат будет тот же три раза подряд, а время на разбор потеряно
Логическая ошибкаПрогон отработал «зелёным», но сделка ушла не тому менеджеру, а сумма посчитана без скидкиЛовится только сверкой контрольных чисел: сколько записей и на какую сумму дошлоПолагаться на статус прогона: зелёный статус означает лишь, что узлы не упали
Ответ 429 — это не «попробуйте ещё раз», а «перестаньте на время»

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

Ретраи: три попытки, растущая задержка

Задержка между попытками должна расти, а не быть постоянной. Постоянная задержка в 10 секунд бесполезна: типовое обновление чужого сервиса длится минуты, а не секунды. Рабочая схема — три попытки с задержкой 30 секунд, 2 минуты и 10 минут. Вместе это закрывает окно в 12,5 минуты и вытаскивает подавляющее большинство коротких отказов, не создавая нагрузки на соседнюю систему.

  1. 1Попытка сразу. Если ошибка относится к типам «недоступность» или «лимит» — переходим к повторам, иначе сразу в очередь.
  2. 2Через 30 секунд. Закрывает мгновенные сетевые обрывы и перезапуск процесса на той стороне.
  3. 3Через 2 минуты. Закрывает короткое обновление и всплеск нагрузки у соседней системы.
  4. 4Через 10 минут. Последняя попытка. Дальше — очередь необработанного и алерт: если сервис лежит четверть часа, он с высокой вероятностью полежит и час, а сценарий не должен висеть в прогоне всё это время.

Отдельно про длинные операции. Если узел отправляет выгрузку на несколько тысяч строк, ретрай целиком означает отправку всей выгрузки заново — и на той стороне может появиться вторая копия всего массива. Такие операции разбивают на части по 100–500 записей с собственным ключом у каждой части: тогда повтор затрагивает только упавший кусок.

Идемпотентный ключ: откуда в CRM пять одинаковых сделок

Что это значитИдемпотентность

Свойство операции давать один и тот же результат, сколько бы раз её ни повторили. Для сценария это означает: если одну и ту же заявку прислали три раза, в CRM всё равно появится одна сделка. Достигается ключом — неизменным идентификатором записи, по которому перед созданием выполняется поиск.

Механизм появления дублей выглядит так. Сценарий отправляет заявку в CRM, CRM успевает её создать, но ответ не доходит из-за обрыва. Для сценария это ошибка, он повторяет — появляется вторая сделка. Три попытки дают до трёх сделок. Если при этом отправитель, не получив подтверждения, продублировал вебхук, в худшем случае получается шесть одинаковых сделок из одной заявки. Менеджеры звонят клиенту дважды, в отчёте месяца лишние строки, а конверсия считается неправильно.

Лечится одним узлом. В качестве ключа берётся то, что задаётся на стороне источника и не меняется: идентификатор письма, номер заявки формы, идентификатор вебхука, номер заказа маркетплейса. Ключ пишется в отдельное поле карточки, а перед созданием сценарий ищет запись по этому полю. Нашёл — обновляет, не нашёл — создаёт. Тот же приём спасает и при обмене с учётной системой: как задваиваются заказы и что с этим делать, разбирали в материале про задвоенные заказы при обмене.

схема процессаobrabotka-oshibok-v-scenariyah--01
Маршрут записи при ошибке: классификация типа, ретраи, очередь необработанного, алерт владельцу

Схема сверху вниз. Верхний блок «Узел вернул ошибку» ведёт в ромб «Какой тип сбоя». Из ромба четыре ветки с подписями: «Недоступность» и «Лимит» ведут в блок «Ретраи: 30 сек → 2 мин → 10 мин»; «Невалидные данные» и «Логическая ошибка» ведут напрямую в блок «Очередь необработанного». Из блока ретраев две стрелки: «удалось» — в блок «Готово, ключ записан» и «не удалось после 3 попыток» — в ту же «Очередь необработанного». Из очереди стрелка вправо в блок «Алерт владельцу: что сломано, что делать, ссылка на прогон» и стрелка вниз в блок «Ежедневная сверка: пришло / обработано / в очереди». Чертёжный стиль, подписи по-русски.

Одна развилка на четыре ветки — вся обвязка помещается в эту схему

Очередь необработанного и ежедневная сверка

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

Ключевое здесь — исходное тело записи целиком. Без него очередь бесполезна: вы знаете, что заявка потерялась, но не знаете, чья она и что в ней было. Хранить тело нужно с той же оглядкой на персональные данные, что и журналы прогонов: срок хранения ограничивается, разбор происходит в тот же день, разобранное чистится.

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

Алерт, который прочитают в тот же день

Алерт в общий рабочий чат читают три дня, потом перестают. Работающий алерт устроен по четырём правилам.

  • Адресный, а не в общий чат. Уходит владельцу сценария из его паспорта, а в его отсутствие — заместителю. Ответственность, размазанная на отдел, равна нулю: у сообщения в общем чате нет получателя.
  • С готовым действием, а не с текстом ошибки. «Сценарий приёма заявок: CRM не отвечает 15 минут, 4 заявки в очереди. Проверить доступность CRM, после восстановления запустить разбор очереди» — это алерт. Строка с кодом исключения — это не алерт, это шум.
  • С порогом, а не на каждую ошибку. Один неудачный прогон из трёхсот — норма, о нём сообщать не нужно. Сообщать надо о трёх подряд, о превышении доли ошибок или о том, что в очереди накопилось больше десяти записей.
  • С проверкой на тишину. Отдельное правило: «сценарий не запускался дольше положенного». Если суточный сценарий не стартовал вовсе, ошибки не будет и обычный алерт не сработает — а данных за сутки не будет тоже.
Молчание страшнее ошибки

Самый дорогой сбой из нашей практики выглядит так: сценарий выключили на время правки и забыли включить обратно. Прогонов нет, ошибок нет, все графики пустые — и именно поэтому никто ничего не заметил. Проверка «не запускался дольше N часов» стоит одного дополнительного сценария и закрывает целый класс потерь, который никакие ретраи не ловят.

Что это стоит и когда обвязка не нужна

Модельный сценарий: приём заявок с сайта в CRM, 600 заявок в месяц, 20 рабочих дней по 9 часов — то есть около 3,3 заявки в час. Внешняя система за год становится недоступной шесть раз в среднем на 3,5 часа: это обновления CRM, работы у провайдера, сетевые проблемы. Считаем год без обвязки и год с ней.

Год без обработки ошибок
6 отказов в год по 3,5 часа при 3,3 заявки в час — теряется 12 заявок за отказ72 заявки
72 заявки, конверсия 20 %, средняя маржа сделки 12 000 ₽172 800 ₽
Дубли без идемпотентного ключа: 3 % от 7 200 записей в год — 216 штук216 дублей
Разбор дублей вручную: 216 штук по 6 минут, 21,6 часа по 700 ₽15 120 ₽
Итого187 920 ₽ в год — и ни одна из этих строк не попадает ни в один отчёт
Год с обвязкой из четырёх элементов
Сборка обвязки на парк сценариев: 16 часов инженера по 3 000 ₽48 000 ₽
Ретраи вытаскивают 58 заявок из 72, оставшиеся 14 уходят в очередь0 ₽ потерь
Разбор очереди: 14 записей по 12 минут, 2,8 часа по 700 ₽1 960 ₽
Ежедневная сверка: 5 минут в день, 250 рабочих дней, 20,8 часа по 700 ₽14 560 ₽
Итого64 520 ₽ за первый год против 187 920 ₽ — разница 123 400 ₽, со второго года разрыв растёт
графикobrabotka-oshibok-v-scenariyah--02
Сравнение: 187 920 ₽ в год без обвязки против 64 520 ₽ в первый год с обвязкой

Столбчатая диаграмма из двух столбцов, ось в рублях от 0 до 200 000. Левый столбец высотой 187 920 подписан «Год без обвязки», разбит на два сегмента: «потерянные заявки, 172 800 ₽» и «разбор дублей, 15 120 ₽». Правый столбец высотой 64 520 подписан «Первый год с обвязкой», разбит на три сегмента: «сборка, 48 000 ₽», «разбор очереди, 1 960 ₽», «ежедневная сверка, 14 560 ₽». Между столбцами стрелка с подписью «123 400 ₽». Под правым столбцом мелкая подпись «со второго года — 16 520 ₽». Подписи по-русски.

Разница в первый год — 123 400 ₽; со второго обвязка стоит только 16 520 ₽

Теперь честная граница. Полная обвязка не нужна трём категориям сценариев. Первая — те, где потеря записи ничего не стоит и обнаруживается сама: выгрузка отчёта в таблицу, ежедневное напоминание в чат, обновление справочника. Здесь достаточно ретраев и одной проверки на тишину. Вторая — сценарии с потоком меньше 20 записей в месяц: 48 000 ₽ на обвязку при таком объёме не окупятся никогда, и дешевле раз в неделю смотреть глазами. Третья — те, где приёмник сам умеет отбрасывать дубли по внешнему ключу; тогда идемпотентность уже обеспечена, и остаются только ретраи с очередью.

И то, что стоит делать всегда, независимо от объёма и цены записи, — суточная сверка трёх чисел. Она стоит пяти минут в день, не требует ни разработки, ни инженера, и отвечает на единственный вопрос, который на самом деле важен: всё ли доехало. Все остальные элементы обвязки лечат уже известную потерю; сверка — единственный способ узнать о неизвестной.

Сценарий не обязан быть безошибочным. Он обязан не терять записи молча.