Exactly-Once Delivery Does Not Exist. Your Consumer Has To Be Idempotent.

Not hard, not expensive, not a premium tier. Impossible, in the proven sense, and it follows from the Two Generals problem. Every product advertising exactly-once is selling at-least-once with deduplication bolted on, which means the work was always going to land on your consumer.

Tech Talk News Editorial9 min read
ShareXLinkedInRedditEmail
Exactly-Once Delivery Does Not Exist. Your Consumer Has To Be Idempotent.

Key takeaways

  • Exactly-once delivery is a proven impossibility in distributed computing rather than an unsolved engineering problem, and the result follows from the Two Generals problem and the FLP impossibility result.
  • What is achievable is effectively-once: at-least-once delivery from the broker combined with idempotent processing in the consumer, so a duplicate delivery changes nothing.
  • At-least-once works by tracking each message until the consumer explicitly acknowledges it, and redelivering if no acknowledgement arrives within a timeout, which is exactly why duplicates are normal rather than exceptional.
  • A dead letter queue is a diagnostic instrument, not a safety net, and its depth is a leading indicator that your processing service level objective is already broken.
  • Retries need exponential backoff with jitter, because synchronised retries from many consumers recreate the overload that caused the failures.
  • A transactional outbox is the pattern for the case where an event must be published exactly when its data is committed, since a database write and a queue publish cannot be made atomic across two systems.

Somewhere in your queue vendor's documentation there is a page about exactly-once delivery. It may be a premium feature. It is not a feature.

Exactly-once delivery is a proven impossibility in distributed computing, and the result follows from the Two Generals problem and the FLP impossibility result.[1] This is not an unsolved engineering challenge that a sufficiently funded team will crack. It is in the same category as sorting faster than the comparison bound.

Which is useful to know, because it tells you where the work has to happen, and it is not in the broker.

Impossible
Exactly-once delivery, provably
At-least-once
What you actually get
Effectively-once
At-least-once plus idempotent consumer
Leading
What DLQ depth indicates, not trailing

Why It Is Impossible, Briefly

The Two Generals problem: two parties must agree on a plan by exchanging messages over a channel that can lose them. Any message can be lost, including the acknowledgement, and including the acknowledgement of the acknowledgement. There is no finite exchange that leaves both sides certain. You can make the probability of confusion small; you cannot make it zero.

Map that onto a queue. A consumer processes a message and sends an acknowledgement. The acknowledgement is lost. The broker, having heard nothing, redelivers. From the broker's position, “processed but the ack was lost” and “never processed” are literally indistinguishable, because the only evidence that would distinguish them is the message that went missing.

So the broker must choose. Redeliver, and risk duplicates. Do not redeliver, and risk loss. That choice is the entire taxonomy: at-least-once or at-most-once. There is no third option, which is the same shape of forced choice as the one CAP describes during a partition.

A lost acknowledgement and unprocessed work look identical from the broker's side. The evidence that would tell them apart is the message that went missing.

Effectively-Once, Which Is Real

The achievable target is effectively-once: at-least-once delivery from the broker, plus idempotent processing in the consumer, so that a duplicate run changes nothing.[1]

The duplicate still arrives. It simply stops mattering. And note where the responsibility landed: on your consumer, not on the queue you are paying for. The mature response is not to hunt for a broker that promises exactly-once, it is to make duplicates harmless.[1]

Which is the same mechanism as idempotency keys on an HTTP endpoint, arriving from a different direction. A message carries an identifier, the consumer records that it processed that identifier in the same transaction as the effect, and a repeat is recognised and skipped. Same problem, same solution, usually built twice by different teams who never noticed.

Why this matters

“Idempotent consumer” is easy to say and easy to get subtly wrong. Recording that you handled message X after committing the effect means a crash in between produces a duplicate. Recording it before means a crash produces a silent loss. It has to be the same transaction as the effect, which is why this is a database design question rather than a queue configuration one.

The Dead Letter Queue Is Not a Safety Net

This reframe is worth the price of admission. A dead letter queue is a diagnostic instrument, and its depth is a leading indicator that your processing objective is already broken.[1]

The failure mode is treating it as a place failures go so they stop paging you. Messages exhaust their retries, land quietly in the DLQ, nothing alerts, and the queue accumulates work that was supposed to happen and did not. Orders unfulfilled. Emails unsent. Nobody finds out until a customer does.

Alert on DLQ depth, and treat a non-zero depth as an incident rather than a backlog to triage on Thursday. This is exactly the kind of signal that needs to be visible rather than merely recorded, which is the argument in observability: data you technically have but nobody looks at is not a capability.

Retries Need Jitter Here Too

Exponential backoff with jitter, for the same reason it belongs in a rate limiter's Retry-After.[1] Consumers usually fail for a shared reason: a dependency is down. So they all fail at once and, without randomisation, they all retry at once, recreating the load spike that caused the failures.

Backoff without jitter converts one outage into a series of synchronised ones. The same pattern appears in rate limiting and in circuit breakers, and it is the single most reusable piece of knowledge in this whole area: anything that makes many clients act on the same schedule is a load generator.

The Case a Queue Cannot Fix: The Outbox

One specific problem worth naming, because it catches people who have done everything else right.

You need to save a record and publish an event about it, and both must happen or neither. But your database and your broker are two separate systems, so there is no transaction spanning them. Write the database and then publish, and a crash between them loses the event. Publish and then write, and a crash loses the data while the world has been told about it.

The transactional outbox resolves this: write the event into an outbox table inside the same database transaction as the data change, and have a separate process read that table and publish from it.[1] Now the commit and the intent to publish are atomic, because they are one write to one system. The publishing step is at-least-once, which is fine, because your consumers are idempotent.

The pattern exists because atomicity across two systems is not available. It is a workaround for a genuine impossibility rather than a nicety, which is why it keeps being reinvented by teams who assumed the queue would handle it.

Takeaway

Exactly-once delivery is provably impossible, so every product claiming it is doing at-least-once with deduplication. Aim for effectively-once instead: accept duplicates and make your consumer idempotent, recording the message identifier in the same transaction as the effect rather than before or after it. Alert on dead letter queue depth and treat it as a leading indicator rather than a backlog. Put jitter in every retry. And when an event must publish exactly when its data commits, use an outbox, because no queue can give you atomicity across two systems.

Sources and further reading

  1. 1.Reporting"Event-driven architecture and message queues: 2026 engineering reference". Source for exactly-once as a proven impossibility rooted in Two Generals and FLP, effectively-once via idempotent consumers, the dead letter queue as a leading indicator, backoff with jitter, and the transactional outbox.
  2. 2.ReportingTyler Treat, "You cannot have exactly-once delivery". The canonical write-up of the impossibility argument and why acknowledgement loss is indistinguishable from non-processing.
  3. 3.Reporting"Background jobs and queues: 2026 engineering reference". Source for the at-least-once acknowledgement-and-timeout mechanism that makes duplicates normal.

Frequently asked questions

Is exactly-once delivery possible?
No, and not in the sense of being difficult. It is a proven impossibility in distributed computing, following from the Two Generals problem and the FLP impossibility result, so no vendor can ship it regardless of engineering budget. Products advertising exactly-once are providing at-least-once delivery with deduplication layered on top.
What is effectively-once delivery?
It is the achievable version: at-least-once delivery from the broker combined with idempotent processing in the consumer, so that receiving the same message twice produces the same result as receiving it once. The duplicate still arrives, it simply stops mattering, which is why the responsibility sits with your consumer rather than with the queue.
Why do message queues deliver duplicates?
Because at-least-once works by holding a message until the consumer explicitly acknowledges it and redelivering if no acknowledgement arrives in time. A consumer that processes a message successfully and then crashes before acknowledging will receive it again, and the broker has no way to distinguish that from a consumer that never processed it at all.
What is a dead letter queue for?
It is a diagnostic instrument rather than a safety net, holding messages that exhausted their retries so you can investigate why. Treating it as somewhere failures quietly go is the mistake, because its depth is a leading indicator that your processing objective is already broken and nobody has noticed.
Do queue retries need jitter?
Yes, for the same reason a rate limiter's Retry-After does. Exponential backoff without randomisation means many consumers retry in synchronised waves, which recreates the load spike that caused the original failures. Jitter spreads those retries so recovery is gradual rather than another thundering herd.
What is the transactional outbox pattern?
It solves the case where an event must be published exactly when its data is committed, which cannot be made atomic because a database and a broker are two separate systems. Instead you write the event into an outbox table inside the same database transaction as the data change, and a separate process reads that table and publishes, so the commit and the intent to publish succeed or fail together.

Written by

Tech Talk News Editorial

Computer engineering background. Writes about software, AI, markets, and real estate, and the places where the three meet.

More about the author
ShareXLinkedInRedditEmail