Гарантии доставки: at-most-once, at-least-once, exactly-once
↑ 5.4 Очереди и брокеры: Kafka, RabbitMQ · ← Предыдущая · → Следующая
Зачем это на собесе
«Что значит exactly-once?» — ловушка: на практике это at-least-once + идемпотентность.
Объяснение
| Гарантия | Поведение | Как достигается | Риск |
|---|---|---|---|
| At-most-once | доставлено 0 или 1 раз | не повторять; коммит до обработки | потеря сообщений |
| At-least-once | доставлено ≥1 раз | повторять до подтверждения; коммит после обработки | дубли |
| Exactly-once | ровно 1 эффект | at-least-once + идемпотентная обработка/транзакции | сложность |
Распределённая система не может отличить «сообщение потеряно» от «ответ потерян», поэтому настоящая доставка «ровно один раз» на уровне сети невозможна. Достижим эффект «ровно один раз»: дубли приходят, но повторная обработка не меняет результат.
Способы получить exactly-once эффект:
- Идемпотентный потребитель: дедупликация по id сообщения (Inbox) или естественно идемпотентные операции (
upsert,set status = paid). - Транзакции Kafka (consume-transform-produce внутри Kafka).
- Transactional outbox для надёжной публикации (см. Transactional Outbox и Inbox, идемпотентные консьюмеры).
- Оптимистическая проверка версии состояния.
// Идемпотентная обработка: таблица обработанных сообщений в той же транзакции, что и эффект
await using var tx = await db.Database.BeginTransactionAsync(ct);
if (await db.Processed.AnyAsync(p => p.MessageId == msg.Id, ct)) return; // дубль
await ApplyAsync(msg, ct);
db.Processed.Add(new Processed(msg.Id));
await db.SaveChangesAsync(ct);
await tx.CommitAsync(ct);Нюансы и подводные камни
- «Exactly-once» в Kafka работает в границах Kafka; внешние эффекты (email, платёж) требуют идемпотентности.
- Дедупликация по id нуждается в TTL и хранилище.
- Порядок и повторы: старое сообщение может прийти после нового — проверяйте версию.
- Побочные эффекты (письма) отправляйте через outbox или идемпотентный ключ провайдера.
Практика
- Реализуйте Inbox-таблицу и проверьте дубль.
- Сымитируйте падение после обработки, но до коммита offset.
- Сделайте обработчик естественно идемпотентным через upsert.
Вопросы с ответами
Возможна ли доставка exactly-once?
На уровне сети нет; достигается эффект exactly-once через at-least-once и идемпотентную обработку.
Почему at-least-once — выбор по умолчанию?
Потеря данных обычно хуже дубля, а дубли снимаются идемпотентностью.