← Назад к списку
ТехническаяBackendSenior

Какие гарантии доставки сообщений бывают? Возможна ли exactly-once и как сделать обработку идемпотентной?

Короткий ответ

  • At-most-once: без повторов, возможна потеря
  • At-least-once: повторы при сбоях, возможны дубли
  • Exactly-once «по проводу» в общем случае недостижима
  • Практика: at-least-once плюс идемпотентный потребитель
  • Дедупликация по ключу сообщения в хранилище потребителя
  • Outbox-паттерн связывает запись в БД и публикацию события

Реальный стандарт — at-least-once с идемпотентной обработкой: дубли неизбежны, но их эффект нейтрализуется.

Как сказать вслух

пример ответа

Есть три уровня: at-most-once — сообщение может потеряться, но не задублироваться; at-least-once — наоборот, точно дойдёт, но возможны повторы; и exactly-once, которая в распределённой системе в чистом виде недостижима — сбой между обработкой и подтверждением всегда возможен. Поэтому на практике берут at-least-once и делают обработчик идемпотентным: например, храним id уже обработанных сообщений и повторы просто пропускаем. А чтобы событие гарантированно ушло вместе с записью в базу, используют outbox-паттерн.

Подробный ответ

Основной ответ

Гарантия определяется поведением при сбое между обработкой и подтверждением. At-most-once: ack до обработки — сбой теряет сообщение. At-least-once: ack после обработки — сбой после обработки, но до ack приводит к повторной доставке. Честная exactly-once-доставка невозможна (это следствие проблемы двух генералов), но достижим exactly-once effect: обработка устроена так, что дубль не меняет результат. Приёмы: естественная идемпотентность операции (UPSERT, установка статуса), таблица обработанных message_id с уникальным ограничением в той же транзакции, что и бизнес-изменение, версионирование агрегатов. Kafka предлагает идемпотентного продюсера и транзакции для цепочек consume-process-produce внутри экосистемы Kafka, но внешние эффекты (письмо, HTTP-вызов) они не покрывают. Для согласованной публикации событий с изменением БД применяют transactional outbox: событие пишется в таблицу outbox той же транзакцией, отдельный процесс или CDC публикует его в брокер.

Ключевые моменты

  • Где стоит ack. Подтверждение до обработки — риск потери, после — риск дубля. Третьего не дано, выбор определяет гарантию.
  • Идемпотентный консьюмер. Уникальный индекс по message_id плюс вставка в одной транзакции с бизнес-логикой — дубль упадёт на конфликте и будет пропущен.
  • Outbox / CDC. Нельзя атомарно закоммитить в БД и опубликовать в брокер — outbox сводит это к одной локальной транзакции.
  • Границы Kafka transactions. Exactly-once в Kafka Streams работает внутри Kafka; отправка email или вызов стороннего API из-под неё не становятся транзакционными.

Практический контекст

Это ядро сеньорских собеседований по распределённым системам: «платёжное событие пришло дважды — что произойдёт?». Ожидается рассказ про идемпотентность на стороне потребителя с конкретной механикой (уникальный ключ, транзакция), а не вера в настройку exactly-once в брокере. Outbox-паттерн — почти обязательное упоминание, когда речь заходит о согласованности базы и шины событий.

Частые ошибки

  • Утверждают, что Kafka даёт exactly-once «из коробки» для любых внешних эффектов
  • Проектируют at-least-once без дедупликации, игнорируя неизбежные дубли
  • Публикуют событие после коммита БД без outbox и теряют его при падении процесса

ИП Кочкин Алексей Сергеевич · ИНН 390509026279 · ОГРНИП 325390000030973 · jiniys2005@yandex.ru