Повторная доставка webhook является нормальным поведением, поэтому обработчик обязан распознавать уже выполненное событие.
Ниже — практический порядок проверки. Он помогает сначала локализовать источник сбоя, затем внести минимальное изменение и проверить результат на реальном сценарии.
Как проявляется проблема
Клиент получает два одинаковых письма об оплате, регистрации или изменении заказа после повторной доставки одного события.
Что проверить в первую очередь
- Зафиксируйте точное время ошибки, пользователя, объект или операцию, на которой она появилась.
- Сравните успешный и проблемный сценарии: входные данные, права, окружение, версию приложения и последовательность действий.
- Проверьте последние изменения в коде, настройках, интеграциях, инфраструктуре и фоновых заданиях.
- Сохраните связанные логи и идентификаторы запроса до повторного запуска или очистки кеша.
Основные причины
Один и тот же внешний симптом может возникать на разных уровнях. Поэтому полезно проверять не только интерфейс, но и данные, права доступа, очередь событий и состояние внешнего сервиса.
- Webhook сразу вызывает отправку без сохранения external event id.
- Уникальность проверяется по времени и типу, а не по идентификатору.
- Две worker-задачи одновременно проходят проверку.
- Провайдер письма принял запрос, но ответ потерялся, и система повторила отправку.
Пошаговая диагностика
- Сравните webhook event id у дублей.
- Проверьте таблицу входящих событий и уникальный индекс.
- Воспроизведите параллельную доставку одинакового payload.
- Сопоставьте message id email-провайдера с внутренним notification id.
Как исправить
Исправление лучше делать небольшими проверяемыми шагами. Перед изменением рабочих данных сделайте резервную копию или подготовьте обратную миграцию.
- Сохраняйте webhook в inbox table с уникальным external id.
- Создавайте уведомление транзакционно один раз.
- Передавайте провайдеру стабильный ключ, если он поддерживается.
- На повторное событие возвращайте успешный ответ без новой отправки.
Как проверить результат
- Многократная доставка одного webhook дает одно письмо.
- Разные бизнес-события по одному заказу не блокируют друг друга.
- Повторите исходный проблемный сценарий и минимум один пограничный случай.
- Проверьте логи после исправления: отсутствие ошибки в интерфейсе еще не гарантирует корректную обработку.
- Убедитесь, что правка не нарушила соседние операции, права других ролей и повторную обработку события.
Чего не стоит делать
- Не отключайте проверки безопасности и разграничение доступа только ради исчезновения ошибки.
- Не меняйте массово рабочие данные без выборки, резервной копии и заранее подготовленного отката.
- Не запускайте повторно платежи, рассылки, возвраты или фоновые задачи, пока не проверена идемпотентность.
- Не оставляйте токены, пароли, персональные данные и полные тела запросов в открытых логах.
Как не допустить повторения
- Проектируйте каждый webhook как at-least-once.
- Храните журнал уведомлений и безопасный механизм повторной отправки.
Что подготовить для разбора
- Ссылку на проблемную страницу, метод API, задание, отчет или интеграцию.
- Точное описание ожидаемого и фактического результата без секретных ключей и паролей.
- Фрагмент лога за нужный период, идентификатор операции и пример входных данных.
- Список последних изменений и информацию о рабочем окружении.
Частые вопросы
Можно ли исправить проблему без полной переделки?
Чаще всего да. Если сначала найти точку расхождения, достаточно локальной правки в проверке, транзакции, обработчике события, настройке или запросе к данным.
Почему ошибка появляется не у всех?
Обычно различаются роль, состояние данных, устройство, регион, способ входа, версия клиента или порядок событий. Поэтому важно получить конкретный воспроизводимый пример.
Итог
Идемпотентность должна охватывать путь от входящего события до фактического сообщения у email-провайдера.
Если самостоятельно локализовать причину не получилось, я могу разобрать логи и код, воспроизвести ошибку, предложить безопасную правку и проверить ее на рабочем сценарии.