Слогер Создать блог
Разработка

Как принимать вебхуки с доставкой at-least-once: дедупликация, идемпотентность и чек-лист для ревью

Контракт, по которому стоит проверять приёмку событий в чужом коде, и критерии, отличающие рабочее решение от демо на один успешный запрос.

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

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

Что означает at-least-once на практике

Отправитель гарантирует, что событие дойдёт хотя бы раз. Про количество он не обещает ничего. Отсюда:

  • одно и то же событие может прийти сколько угодно раз;
  • дубль способен приехать через 24 часа и после повторной сериализации — с другим порядком ключей или другими пробелами;
  • дубли бывают одновременными, в том числе из разных процессов;
  • процесс могут перезапустить в любой момент без предупреждения;
  • любой ответ 2xx отправитель считает окончательным успехом, всё остальное приводит к новой попытке.

Вывод один: обработчик обязан быть идемпотентным. Ответ «уже видел» — тоже успех, а не ошибка.

Контракт: три эндпоинта

  1. POST /webhooks/orders — применяет изменение состояния не более одного раза и возвращает одинаковое тело на дубли.
  2. GET /orders/:id — отдаёт текущий заказ и stateChanges, то есть сколько раз изменение реально применилось.
  3. GET /health — дешёвая проверка готовности.

Счётчик stateChanges здесь ключевой. Без него не отличить сервис, который корректно гасит дубли, от сервиса, который молча их проглатывает и теряет события.

Ключ дедупликации

Полный балл получает тот, кто сначала смотрит на явный идентификатор события в заголовках и берёт его, а если такого нет — канонизирует тело и хэширует результат.

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

И деталь, которую упускают чаще всего: HTTP-слой должен читать сырое тело запроса. Если распарсить JSON и закодировать его заново перед хэшированием, байтовая стабильность ломается ровно у тех клиентов, которые не присылают id события.

Атомарность держит база, а не код

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

Гарантию даёт уникальный индекс или первичный ключ, а не логика приложения. Вставка записи дедупликации и изменение состояния коммитятся в одной транзакции. Вставка идёт с ON CONFLICT DO NOTHING, и решение принимается по числу затронутых строк: ноль означает, что событие уже было — отдаём сохранённое ранее тело ответа; единица — применяем изменение.

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

HTTP и семантика повторов

Дубль должен получать 200 и то же самое тело, что и первая доставка. Не 409, не 500 и не тело, которое меняется от попытки к попытке. Отправитель трактует 2xx как окончательный успех, а любой другой код — как повод повторить. Ответ 409 на дубль выглядит логично, но запускает бесконечные ретраи.

Тесты и эксплуатация

Минимум для зачёта: одновременные дубли и перезапуск процесса, причём обязательно против настоящего хранилища. Хэппи-путь с замоканной базой не проверяет ничего из того, ради чего задача вообще существует — мок всегда ответит так, как вы его запрограммировали.

Отдельная часть оценки — операционные заметки. Что будет с таблицей событий через год? Какое окно хранения дедупликации выбрано и почему? Есть ли метрика доли дублей? Молчание по этим пунктам означает, что про судьбу данных не думали.

Рубрика для сравнения решений

Что оцениваемВесПолный баллМгновенный ноль
Вывод ключа дедупликации20Явный id события, иначе канонический хэшХэш сырого тела
Ровно один побочный эффект25Вставка ключа и изменение состояния в одной транзакции под уникальным индексомПроверка, затем вставка в коде приложения
Живучесть после рестарта15Состояние дедупликации переживает смерть процессаSet или Map в памяти
HTTP и повторы15200 на дубль с исходным телом ответа409, 500 или тело, меняющееся между попытками
Тесты, которые это ловят15Одновременные дубли и рестарт против настоящего хранилищаТолько хэппи-путь с замоканной базой
Операционные заметки10Окно хранения, рост таблицы, метрика доли дублейПро судьбу данных через год ни слова

Ниже 60 баллов обычно означает, что при штатном поведении ретраев сервис либо потеряет события, либо применит их дважды. Выше 85 получают решения, где автор объясняет, почему гарантией служит уникальный индекс, а не проверка в приложении.

Чек-лист перед мержем

  • Ключ события: явный идентификатор из заголовка, иначе хэш канонизированного тела.
  • Уникальный индекс в базе как единственная гарантия, а не SELECT перед INSERT.
  • Сохранённое тело ответа: дубль получает тот же ответ, что и первая доставка.
  • Чтение сырого тела до парсинга.
  • Отдельный тест на одновременные дубли и отдельный — на рестарт.
  • Окно хранения и метрика доли дублей описаны явно.

Разница между демо и надёжной приёмкой видна ровно на дублях. Всё остальное — вопрос аккуратности, который проверяется за один взгляд на схему данных и на тесты.

По материалам: career. Текст переработан редакцией Слогера.

← На главную

Рекламное место — Конец поста
Реклама · Слогер

Комментарии (0)

Войдите, чтобы комментировать.

Пока нет комментариев. Будьте первым.

Карьера

Почему ваша работа кажется скучной и как рассказать о ней на собеседовании

Тикет «пофиксил таймаут экспорта» скрывает неделю расследований и выбор между двумя неидеальными вариантами. Разбираем, как восстановить эту историю и не придумывать проценты ради красивого слайда.

Слогер 30.10.2026 ▲ 0
Карьера

Что делать, если дата увольнения попала на праздничную неделю: разбор WARN-данных

Неделя с 26 ноября 2026 года собрала 5 819 запланированных сокращений — третье место среди предстоящих недель. Разбираю, как читать такие цифры и что успеть сделать, если дата вступления в силу выпала на праздники.

Слогер 30.10.2026 ▲ 0
AI

Автоматизация бизнеса с помощью ИИ: 5 примеров

А что, если просто рассказать ИИ о своём бизнесе, а он сам предложит, как его автоматизировать? 🤔 Мы добавили такую возможность в Fluxs и решили проверить, насколько хорошо она работает. Взяли пять разных бизнесов: от пекарни до юридической компании. ИИ самостоятельно подобрал инструменты и собрал схемы. Но без сюрпризов не обошлось. Например, для автосервиса он придумал блок «Запись на ремонт», которого у нас вообще нет. А салону красоты зачем-то предложил голосового бота. Зато с интернет-магазином справился почти идеально. Получился интересный эксперимент. Теперь мы не только проверили ИИ, но и поняли, каких функций не хватает в конструкторе. Подробности и все пять примеров — в статье. #ИскусственныйИнтеллект #АвтоматизацияБизнеса #Нейросети #Fluxs

Слогер 29.10.2026 ▲ 1
Fluxs lenta
Реклама · fluxs.ru
Стартапы

Как продать первую услугу, когда тебя никто не знает: 29 писем, 7 постов и два платежа по $5

ИИ-агент по имени Суки проверяет цифры и утверждения по первоисточникам за пять долларов и выложил полный отчёт о выручке, включая нули. Что в этих числах полезно каждому, кто запускает микро-услугу с нуля.

Слогер 29.10.2026 ▲ 1
Карьера

Разбор: WARN Act по-гавайски — 60 дней на уведомление, доплата к пособию и штраф $500 в день

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

Слогер 29.10.2026 ▲ 1
Разработка

Как AI-код-ревью ускоряет онбординг разработчика — и когда мешает учиться

Большая часть онбординга происходит в комментариях к пулл-реквестам, а не в документации. Разбираю, что даёт AI-ревью новичку, откуда оно берёт неписаные конвенции команды и где начинается вред.

Слогер 29.10.2026 ▲ 1
Карьера

Почему увольнение 30 человек не требует предупреждения за 60 дней: разбор WARN и что делать сотруднику

Федеральный закон США срабатывает только на крупных сокращениях, а мелкие проходят мимо — и не попадают в статистику. Разбираем пороги, оговорку про 90 дней и шаги, которые доступны уволенным.

Слогер 29.10.2026 ▲ 1
Fluxs lenta
Реклама · fluxs.ru
Маркетинг

Как запустить продукт на маленькой базе подписчиков: 5 писем, которые продают без фальшивого дефицита

Одно письмо-анонс почти никогда ничего не продаёт — даже на списке из 40 человек. Разбираем пятидневный ритм рассылки, где каждое письмо решает свою задачу, и даём готовый шаблон последнего письма.

Слогер 28.10.2026 ▲ 1
Разработка

Как выгрузить вакансии и зарплатные вилки из Greenhouse API: разбор подводных камней

Любая доска Greenhouse отдаётся публичным JSON без ключа — целиком, одним ответом. Но чтобы данные были пригодны, нужно знать пять вещей про флаги, экранирование и двойной счёт вакансий.

Слогер 28.10.2026 ▲ 0
Маркетинг

Переезд (миграция) с Битрикс24 и amoCRM без потери данных: в Fluxs появился модуль миграции

4 000 контактов в Битрикс24. Сотни сделок. Задачи за несколько лет. И всё это нужно перенести в новую CRM? Раньше такой переезд мог занять несколько дней. А иногда именно из-за переноса данных компании годами оставались в неудобной системе. Мы решили эту проблему. 🚀 В Fluxs появился модуль «Миграция данных». Теперь можно переносить контакты, сделки, задачи, проекты и базу знаний из Битрикс24, amoCRM, Jira, Яндекс Трекера и Confluence. А если вашей системы нет в списке — загрузить данные через Excel, CSV или JSON.

fluxs.ru 27.10.2026 ▲ 1
Fluxs lenta
Реклама · fluxs.ru
Карьера

Чем Cal-WARN отличается от федерального WARN: разбор порогов, уведомлений и штрафов

Калифорния требует предупреждать о массовых сокращениях за 60 дней — как и федеральный закон США. Только порог ниже, парт-тайм считается, а правило одной трети не действует. Разбираем, что это меняет для работника и работодателя.

Слогер 27.10.2026 ▲ 1