At-most-once, at-least-once, duplicates, and ordering
coreadvancedA queue offers one of two honest guarantees, and the difference is *when the message is acknowledged*. **At-most-once** acknowledges before running: a crash loses the work, and nothing is ever duplicated. **At-least-once** acknowledges after completing: a crash redelivers, so a message may be processed more than once. There is no third option available in practice — "exactly-once delivery" is not something a network can provide, because the acknowledgement itself can be lost. What you *can* build is exactly-once **effect**, by making the consumer idempotent so a second delivery changes nothing. Ordering is a separate and weaker promise than most people assume: with more than one consumer, or with retries, messages routinely arrive out of order.
Think of it as
Both guarantees are the same bet placed on opposite sides of a coin you cannot see: acknowledge early and you bet the worker survives; acknowledge late and you bet you can tolerate a repeat. Neither bet can be avoided, so the only real decision is which failure your work can absorb — and duplication is almost always the one to choose, because you can engineer around it while lost work is simply gone. That is what the section's closing instruction means: designs that assume exactly-once execution are assuming a guarantee nothing in the stack is providing, and they fail rarely enough that the assumption survives review and long enough for the failure to be expensive. Ordering deserves the same scepticism. A single queue with a single consumer processes in order; add a second worker and messages are handled concurrently, add a retry and a failed message is re-queued behind newer ones, and any of those makes "the update after the create" arrive first. So do not encode ordering assumptions into consumers — carry a version or a timestamp and let each message decide whether it is still relevant, or key work so that a stale message is a no-op.
What we're doing: A consumer that is safe against both duplication and reordering — the two things at-least-once actually gives you.
- 2–3
- The producer's own event id, not a timestamp or a hash of the payload — two genuinely distinct events can carry identical contents.
- 10–12
- The claim and the work share a transaction, so a crash between them rolls both back and the redelivered message legitimately runs again.
- 13–15
- The `IntegrityError` is the duplicate path, and returning normally acknowledges it — retrying would repeat work already done.
- 20–24
- `version__lt=version` is the reordering guard: an older event arriving after a newer one matches no rows and quietly does nothing.
Why this works: At-least-once gives you two problems, not one — the same message twice, and messages out of order — and they need different fixes: a unique constraint for the first, a conditional update for the second.
Assuming messages arrive in the order they were sent
Wrong
Better
What you see: Prices and statuses occasionally revert to an earlier value. It correlates with worker count and with retries, so it appears when you scale up and never in a single-worker environment.
Why: Ordering is only guaranteed with one consumer and no retries. Two workers pull from the same queue concurrently, and a message that failed and retried re-enters behind messages published after it — so an older update can be applied last. Carrying a version and refusing to apply anything not strictly newer makes each message decide its own relevance, which is the only approach that survives concurrency.
- At-most-once — ack first
- The broker forgets the message before the work starts.
- A worker killed mid-task loses it: no error, no retry, no log line.
- Nothing is ever processed twice.
- Fine for a metrics ping or a cache warm — work that is cheap to lose.
- Wrong for anything with a customer-visible effect.
- At-least-once — ack after
- The broker keeps the message until the task returns.
- A worker killed mid-task means redelivery, not loss.
- The task may therefore run twice — that is the price.
- Deduplicate on a stable id to get exactly-once effect.
- The right default for anything that matters.
The two real guarantees, and the one that is not on offer
Together
Remember: A queue gives you at-most-once (ack first, lose on crash) or at-least-once (ack after, repeat on crash) — never exactly-once delivery, because an acknowledgement can be lost. Choose at-least-once and engineer exactly-once *effect*: deduplicate on the producer's own identity, with a unique constraint inside the same transaction as the work. Then handle the second problem separately — ordering holds only with one consumer and no retries, so carry a version and let a conditional update make a stale message a no-op.
See also: retries dead letter and poison messages · task idempotency monitoring and recovery · at least once delivery and consumer idempotency

