Exactly-once delivery — гарантия, что сообщение обработается ровно один раз. В распределённых системах это святой грааль, потому что в чистом виде невозможен. На практике используют exactly-once semantics — комбинацию at-least-once delivery и идемпотентной обработки.
Три уровня гарантий:
- At-most-once: отправил и забыл. Может потеряться.
- At-least-once: retry до подтверждения. Может быть дубликат.
- Exactly-once: ровно один раз. Достигается через идемпотентность.
func handleEvent(ctx context.Context, event Event) error {
// идемпотентность через уникальный ID события
if processed, _ := redis.Exists(ctx, "event:"+event.ID); processed {
return nil // уже обработано
}
err := processEvent(ctx, event)
if err != nil {
return err
}
redis.Set(ctx, "event:"+event.ID, true, 7*24*time.Hour)
return nil
}
Kafka поддерживает exactly-once через transactional producer + consumer в одной consumer group. Но между сервисами — только at-least-once + idempotency. Храни ID обработанных событий и проверяй перед обработкой.