Stackbook Logo
messagingestablished · medium operational burden

Exactly-Once Illusion

Also known as: exactly-once-delivery-myth, at-least-once-plus-idempotence

Intent

Achieve exactly-once effects by combining at-least-once delivery with idempotent processing, because exactly-once delivery itself is not possible over unreliable networks.

Problem

After a timeout, a sender cannot tell whether a message was lost or only its acknowledgment was. It must either retry and risk duplicates, or not retry and risk loss.

Forces

  • •Networks drop messages and acknowledgments
  • •Senders cannot distinguish a lost request from a lost reply
  • •Duplicates cause double charges and double emails
  • •Losing messages is usually worse than duplicating them

Solution

Overview

Exactly-once delivery over an unreliable network is impossible: it is a variant of the Two Generals problem. What systems offer is exactly-once effect, built from at-least-once delivery plus idempotent handling. The illusion is that each message was delivered once; the reality is that duplicates arrived and were harmlessly ignored.

Think of it as two separate guarantees: the transport promises nothing is lost, and the receiver promises repeats change nothing. Together they produce the observable behavior of exactly one effect per message.

How it works

The sender retries until it sees an acknowledgment. The receiver tracks which messages it has already applied.

producer --msg(id=42)--> consumer
   (ack lost)
producer --msg(id=42)--> consumer   retry

consumer:
  BEGIN
    if exists(processed, 42) then skip
    else apply(effect); insert(processed, 42)
  COMMIT
  send ack

The effect and the processed marker must commit atomically. For HTTP APIs, clients send an idempotency key and the server returns the stored result on repeats. Kafka's idempotent producer and transactions apply the same principle inside the Kafka ecosystem, but external side effects still need their own protection.

Pitfalls

  • Marking the message processed before the effect, or vice versa, outside a transaction.
  • Expiring dedupe keys sooner than the longest possible retry or redelivery delay.
  • Calling a non-idempotent third party inside the handler with no key of its own.
  • Treating broker settings as end-to-end guarantees; they cover only the broker boundary.
  • Ordering assumptions: duplicates can arrive after newer messages.

Key takeaways

  • Exactly-once delivery is impossible; exactly-once effect is achievable.
  • Use at-least-once delivery with idempotent consumers.
  • Commit the dedupe marker atomically with the effect.
  • Size the dedupe window to the longest retry horizon.

✓ When to Use

  • •Payments, orders, and any non-repeatable side effect
  • •Message consumers with retries or redelivery
  • •APIs where clients retry on timeout

✗ When Not to Use

  • •Purely idempotent or read-only operations
  • •Best-effort telemetry where loss or duplication is harmless
  • •When messaging semantics already guarantee atomic effects within one transactional system

Pros

  • +Correct under retries and crashes
  • +Works over any unreliable transport
  • +Simple to reason about once keys exist

Cons

  • −Requires durable deduplication state
  • −Every consumer must implement idempotence
  • −Retention window for keys limits protection

Cost Profile

Infrastructure

Low: storage for dedupe keys

Operational

Medium: expire and monitor dedupe state

Cognitive

Medium: idempotent design discipline

Failure Modes

  • Dedupe record and side effect not committed atomically

  • Idempotency keys expire before a late retry arrives

  • Non-idempotent external call hidden inside the handler

Real-World Examples

Alternatives

  • exactly-once-processing

Related Patterns

  • idempotency-key
  • idempotent-receiver
  • exactly-once-processing
  • transactional-inbox
  • outbox-pattern
  • retry-with-backoff
  • dead-letter-queue

Competency Domains

distribution communicationdata state