Exactly-once vs at-least-once processing

At-least-once: every message is processed one or more times (retries can cause duplicates). Exactly-once: every message is processed precisely once — no duplicates, no drops. Exactly-once is harder to achieve in distributed systems; at-least-once is simpler but requires idempotent handling of duplicates.

At-least-once: retries can duplicate

sequenceDiagram participant Q as Queue participant C as Consumer participant DB as DB Q->>C: Message M C->>DB: Process M DB-->>C: (slow / timeout) C->>Q: (no ack - timeout) Q->>C: Redeliver M C->>DB: Process M again Note over DB: Duplicate if not idempotent

Exactly-once: dedupe or transactional outbox

flowchart TB subgraph E1["Approach 1: Idempotent consumer"] I1[Store processed message IDs] I2[Skip if ID seen] end subgraph E2["Approach 2: Transactional outbox"] O1[Write to DB + outbox in same txn] O2[Relay reads outbox, publishes once] end
SemanticGuaranteeHow
At-least-onceNo message lost; may duplicateRetry until ack; consumer idempotent
Exactly-onceProcessed onceIdempotency keys, transactional outbox, or system support (e.g. Kafka exactly-once)

Design for at-least-once and make consumers idempotent (e.g. by message ID or business key). Add exactly-once semantics only where needed (e.g. payments) using deduplication or transactional patterns.