Deep dive
Delivery semantics describe what happens under failures and retries:
- **At-most-once**: no duplicates, but messages can be lost.
- **At-least-once**: no loss (ideally), but duplicates can happen.
- **Exactly-once**: no loss and no duplicates — very hard end-to-end once you include external side effects.
Because retries and timeouts are inevitable, many real systems choose at-least-once and rely on **idempotent consumers**.
How to make consumers idempotent
- Deduplicate by message id (store processed ids for a retention window).
- Use idempotency keys for commands (e.g., “charge card” with a unique key).
- Use DB constraints/upserts to make repeated processing a no-op.
- Use the outbox pattern to coordinate DB writes and message publishing.
Common pitfalls
- “Exactly-once” in one component doesn’t guarantee end-to-end exactly-once.
- Side effects (emails, payments) must be guarded with idempotency keys.
- Dedup stores can become hot spots without good partitioning/