Успешный HTTP-запрос и успешно выполненная бизнес-операция не всегда одно и то же. Разберём безопасный повтор на примере CRM.
Содержание 10
Схема материала
Сбой не сообщает, где остановилась операция
Представьте запрос на создание сделки. CRM сохранила запись, но соединение оборвалось до ответа. Для отправителя это тайм-аут, хотя бизнес-действие уже выполнено. Если просто отправить тот же запрос ещё раз, получится дубль. Обратная ситуация тоже возможна: запрос не дошёл вовсе. По одному сетевому исключению нельзя выбрать между этими вариантами. Поэтому восстановление нужно проектировать вместе с основным сценарием. Надпись «повторить при ошибке» без описания последствий оставляет важнейшую часть процесса на удачу.
Найдите идентичность события
У заявки должен быть ключ, который сохраняется при повторной доставке. Это идентификатор события источника или ключ операции, созданный до первого запроса. Не используйте случайный UUID на каждой попытке: тогда повторы будут выглядеть как новые действия. Телефон и время отправки тоже не всегда подходят — человек может законно создать две заявки. Запишите, что именно считается одной операцией, как долго хранится ключ и какие поля должны совпадать. Повтор с тем же ключом, но другим содержимым лучше отклонить и отправить на разбор.
Сохраните намерение раньше внешнего действия
Прежде чем обращаться к CRM, зарегистрируйте входящее событие в устойчивом хранилище. Уникальное ограничение на ключ защищает от одновременной обработки двумя специалистами. После успешного запроса сохраните внешний идентификатор и итог шага. Если процесс остановился между этими действиями, восстановление должно уметь проверить внешний результат по тому же ключу. Там, где API поддерживает идемпотентность, используйте её по договору сервиса. Там, где не поддерживает, заранее определите безопасную стратегию проверки или ручного разрешения неопределённости.
Не все ошибки заслуживают повтора
Временная недоступность и ограничение частоты обычно допускают повтор с паузой. Ошибка формата поля или отозванные права доступа требуют исправления, а не тысячи одинаковых попыток. Для каждого класса ошибок укажите предел попыток, задержку и владельца. Учитывайте рекомендации внешнего API по повтору и не увеличивайте нагрузку в момент его сбоя. После исчерпания лимита событие должно оставаться доступным для разбора. Потерянное событие и бесконечная очередь одинаково неудобны, просто второе выглядит более живым.
Минимальный пример для обсуждения
INSERT INTO incoming_events (event_key, payload_hash, status)
VALUES (:event_key, :payload_hash, 'received')
ON CONFLICT (event_key) DO NOTHING;
-- Затем сравнить payload_hash и читать сохранённый результат.
-- Новая попытка использует прежний event_key.Журнал нужен оператору, а не только разработчику
Полезная запись объясняет, какая заявка обрабатывается, какой шаг выполнен, что неизвестно и какое действие безопасно. Не обязательно показывать полный технический ответ сервера каждому сотруднику. Достаточно понятного статуса, времени следующей попытки и ссылки на подробности для ответственного. Секреты, токены и лишние персональные данные в журнале не нужны. Кнопка ручного повтора должна запускать тот же защищённый путь, а не обходить ограничения. Иначе именно аварийное восстановление станет главным источником дублей.
Проверяйте неприятные места специально
Успешный прогон одной заявки проверяет только счастливый путь. Добавьте двойную доставку, два параллельных обработчика, остановку после внешнего действия, недоступную CRM и неверное поле. Для каждой проверки заранее запишите ожидаемое количество бизнес-записей и конечное состояние события. Восстановите процесс после перезапуска и убедитесь, что журнал не потерял связь с исходной заявкой. На учебном стенде можно искусственно обрывать соединение; в рабочей системе такие эксперименты требуют согласованного безопасного окружения и ограничения воздействия.
Согласуйте границы автоматизации
Не каждый неоднозначный случай нужно разрешать автоматически. Изменение суммы, повторная оплата или отправка клиенту юридически значимого уведомления могут требовать отдельного подтверждения. Укажите эти границы в схеме процесса и инструкции оператору. Хорошая автоматизация сокращает рутину, но сохраняет человеку возможность понять и остановить действие. В кейсе одна заявка доставляется три раза для проверки единственности результата. Этот пример показывает принцип тестирования и не обещает универсальную гарантию для любого API: свойства внешней системы всегда нужно изучать отдельно.
Для оператора полезно показать отдельно число технических попыток и число созданных бизнес-записей. Три попытки могут быть нормальным восстановлением одной заявки. Три сделки для одной заявки — уже нарушение согласованного инварианта, которое требует разбора.
Проверить перед применением
- ☐Определены задача и последствия ошибки.
- ☐Известно происхождение данных и разрешённый способ их использования.
- ☐Проверки можно повторить на той же версии входных материалов.
- ☐Ограничения результата перечислены рядом с выводом.
Загружаем комментарии…