What is At‑Least‑Once Delivery?
A complete, beginner‑to‑production guide to one of the most important delivery guarantees in distributed messaging systems — what it means, how it works internally, and how companies like Amazon, Uber, and Netflix rely on it every single day.
Introduction and History
At‑least‑once delivery is the simple, opinionated promise that a message will always arrive at its destination — even if it occasionally arrives more than once — and that single promise is what quietly holds together most of the world’s reliable messaging infrastructure.
Imagine you send a WhatsApp message to a friend, but your phone loses network signal for a second right after you hit send. Does the message vanish forever? Does it get sent twice? Or does it arrive exactly once, no matter what? The answer to this question is governed by something called a delivery guarantee, and one of the three fundamental guarantees in distributed messaging is called at‑least‑once delivery.
At‑least‑once delivery is a promise that a message‑passing system makes to its users: “I guarantee that your message will be delivered to the recipient at least one time. It might occasionally be delivered more than once, but it will never silently disappear.” This single sentence sits at the heart of nearly every reliable messaging system built in the last three decades — from early enterprise message queues like IBM MQSeries in the 1990s, to modern systems like Apache Kafka, Amazon SQS, RabbitMQ, and Google Cloud Pub/Sub.
Think of a registered postal courier who is instructed: “Do not return to the depot until the customer has signed for the parcel.” If the courier rings the doorbell and there’s no answer, they don’t give up — they try again tomorrow. If the customer signs but the courier’s tracking scanner glitches and doesn’t record the signature, the courier’s supervisor might send the parcel out for delivery again, even though the customer already has it. The customer might receive two identical parcels — mildly annoying, but never zero parcels. That is exactly what at‑least‑once delivery guarantees: delivery is certain, duplication is possible.
A brief history
The need for delivery guarantees emerged in the 1980s and 1990s as enterprises started connecting mainframes, banks, and retail systems over unreliable networks. Early message‑oriented middleware (MOM) products such as IBM MQSeries (1993) and Microsoft MSMQ (1997) were built specifically to solve the “what happens if the network drops mid‑message” problem. Engineers realised that networks are inherently unreliable — packets get lost, servers crash mid‑operation, and acknowledgments themselves can go missing. Out of this reality, three formal delivery semantics were defined:
- At‑most‑once — a message is delivered zero or one times. It might get lost, but it’s never duplicated.
- At‑least‑once — a message is delivered one or more times. It’s never lost, but it might be duplicated.
- Exactly‑once — a message is delivered precisely one time. The holy grail, and the hardest to achieve.
As the internet scaled in the 2000s and 2010s, and as companies like LinkedIn (which created Kafka), Amazon (which created SQS), and Google (which created Pub/Sub) started building planet‑scale systems, at‑least‑once delivery became the default, pragmatic choice for most production messaging infrastructure — because, as we’ll see, it is dramatically simpler and cheaper to implement than true exactly‑once delivery, while still being “safe” in the sense that no data is ever silently lost.
1993 — IBM MQSeries
One of the first widely deployed message‑oriented middleware products; introduced durable queues with acknowledgment‑based redelivery.
1997 — Microsoft MSMQ
Brought guaranteed‑delivery queues to the Windows enterprise stack for banks, retail, and government systems.
2004 — Amazon SQS launches
One of AWS’s earliest services, built explicitly on at‑least‑once delivery as its default guarantee.
2011 — Apache Kafka open‑sourced
LinkedIn releases Kafka; at‑least‑once between producers, brokers and consumers becomes the industry‑standard defaults.
2017‑present — “Exactly‑once semantics”
Kafka, Pub/Sub and others ship transactional/EOS features — still at‑least‑once transport underneath, dressed up with dedup and atomic offset commits.
The Problem and Motivation
To understand why at‑least‑once delivery exists, we need to understand the problem it solves: networks and machines are fundamentally unreliable. Any of the following can happen at any moment in a distributed system:
- A network cable gets unplugged or a packet gets dropped mid‑flight.
- A server crashes right after processing a message but before sending an acknowledgment.
- A load balancer times out a request that was actually still being processed.
- A consumer service is deployed (restarted) at the exact moment it was handling a message.
- A message broker itself crashes and has to fail over to a replica.
In a perfect world, every “send message” operation and its corresponding “receive and process” operation would happen atomically, instantaneously, and without failure. In the real world, there is always a window during which the sender does not know whether the receiver got the message. This is a classical problem in distributed systems theory, closely related to the Two Generals’ Problem, which proves mathematically that two parties communicating over an unreliable channel can never be perfectly certain that a message was received, no matter how many acknowledgments they exchange.
Because it’s provably impossible to guarantee in the general case over an unreliable network without additional coordination (deduplication, idempotency, transactional writes). What most systems that advertise “exactly‑once” actually deliver is at‑least‑once delivery + deduplication, which produces exactly‑once processing effects even though the underlying transport may still redeliver messages. We’ll cover this distinction in detail later.
The motivation for choosing at‑least‑once
Given that perfect delivery is impossible, system designers must choose which side to err on: lose messages occasionally (at‑most‑once) or duplicate messages occasionally (at‑least‑once). For the overwhelming majority of business systems — payments, order processing, notifications, event pipelines, IoT telemetry — losing data is far worse than processing it twice. A duplicate order‑confirmation email is a minor annoyance; a lost order confirmation is a customer‑service disaster and potentially a lost sale. This asymmetry is why at‑least‑once delivery became the industry‑standard default.
Beginner example
Suppose you’re building a simple “Order Placed” notification feature. When a customer places an order, your backend publishes an event to a queue, and a separate email service consumes that event and sends a confirmation email. If the queue guarantees at‑least‑once delivery, you are guaranteed the email service will see that event at least once — but your email‑sending code must be written carefully so that if the event arrives twice, the customer doesn’t get two confirmation emails.
Production example
Uber’s trip‑processing pipeline ingests millions of GPS location pings, trip‑state changes, and payment events per minute using Kafka, which is configured for at‑least‑once delivery between producers, brokers, and consumers. Uber’s downstream services (fare calculation, driver payouts, surge pricing) are all built to tolerate duplicate events through idempotency keys, because losing a single GPS ping or payment event could mean an incorrect fare or a driver not getting paid — a business risk far worse than doing a calculation twice.
The Two Generals’ Problem, in plain language
Picture two army generals camped on opposite hills, both needing to attack an enemy city at the exact same time to win, but able to communicate only by sending messengers through enemy territory, where a messenger might be captured and never arrive. General A sends a messenger saying “attack at dawn.” Even if the messenger gets through, General A can never be sure it arrived — so General B sends a confirmation messenger back. But now General B can’t be sure that messenger got through either, so logically General A should confirm receipt of the confirmation, and so on forever. Mathematically, no finite number of messages can produce perfect mutual certainty over an unreliable channel. This isn’t an engineering limitation that better technology will eventually fix — it’s a provable, permanent fact about communicating over channels where messages can be lost. At‑least‑once delivery is the pragmatic, engineering answer to a problem that formal logic tells us has no perfect solution: instead of chasing impossible certainty, keep resending until you get an acknowledgment, and design the receiving side to not care if it hears the same thing twice.
Core Concepts
A handful of small vocabulary items — delivery semantics, ACKs, retries, idempotency, deduplication, and message identity — do almost all the intellectual work in this space. Nail these, and everything else in the guide slots into place.
3.1 The three delivery semantics, explained
| Semantic | What it means | Failure mode | Complexity |
|---|---|---|---|
| At‑most‑once | Message sent, no retry on failure | Message may be lost | Low |
| At‑least‑once | Message resent until acknowledged | Message may be duplicated | Medium |
| Exactly‑once | Message processed precisely one time | Requires dedup + transactions | High |
3.2 What “delivery” actually means
It’s important to be precise about what is being guaranteed. “Delivery” in at‑least‑once systems typically refers to the message reaching the consumer’s processing logic at least once — not necessarily that the consumer’s business logic executed successfully exactly once. The guarantee lives at the transport/broker layer; what happens after the message lands in your application is your responsibility.
3.3 Acknowledgment (ACK)
The mechanism that makes at‑least‑once delivery work is the acknowledgment, often abbreviated ACK. When a consumer receives and successfully processes a message, it sends an ACK back to the broker or queue. Only after receiving this ACK does the broker consider the message “done” and remove it (or mark it as processed). If no ACK arrives within a configured timeout — because the consumer crashed, the network dropped the ACK, or processing took too long — the broker assumes the worst and redelivers the message.
Think of a restaurant kitchen with an order‑ticket system. The waiter (producer) clips an order ticket to the rail (queue). A chef (consumer) takes the ticket and starts cooking. Only when the chef calls out “order up!” (ACK) does the ticket get removed from the rail. If the chef gets distracted and never calls “order up,” the head chef assumes the ticket was lost or forgotten and puts a duplicate ticket back on the rail for someone else to cook — better a duplicate dish than a customer who never gets fed.
3.4 Retry and redelivery
Retry is the mechanism, redelivery is the outcome. Systems that implement at‑least‑once delivery must define: how long to wait before assuming failure (the visibility timeout or ACK timeout), how many times to retry, and what to do with a message that fails repeatedly (send it to a Dead Letter Queue, covered later).
3.5 Idempotency
Because at‑least‑once delivery can redeliver a message, the receiving application must be written so that processing the same message multiple times produces the same end result as processing it once. This property is called idempotency, and it is the single most important concept a developer must understand to safely build on top of at‑least‑once systems.
An operation is idempotent if doing it once, or doing it five times, leaves the system in exactly the same state. Pressing an elevator call button five times doesn’t summon five elevators — the button is idempotent. Charging a credit card five times for the same order is not idempotent unless you specifically design it to check “have I already charged this order?” before charging again.
3.6 Deduplication
Deduplication is the practical technique used to achieve idempotency at the infrastructure level: the consumer keeps track of message IDs it has already processed (in a database, cache, or dedup table) and skips any message whose ID it has seen before, even if the broker redelivers it.
3.7 Message identity: why the ID matters so much
Everything in at‑least‑once delivery hinges on being able to reliably answer the question: “have I seen this exact logical message before?” That answer is only as good as the message ID used to ask it. A common beginner mistake is to let the broker auto‑generate a message ID on each publish — if the producer itself retries a publish call (because it didn’t receive a broker ACK, even though the broker actually stored the message), a broker‑generated ID will treat the retried publish as a brand‑new, different message, defeating deduplication before it even starts. The fix is for the producer to generate a stable, business‑meaningful ID — for example, a UUID generated once when the order is created, or a natural key like orderId + eventType — and to always publish using that same ID on every retry.
3.8 At‑least‑once vs “effectively‑once”
You’ll frequently see vendor marketing use the term “effectively‑once” or “exactly‑once semantics” (EOS). These terms almost always describe an at‑least‑once transport combined with a deduplication or transactional‑commit layer bolted on top, rather than a fundamentally different delivery mechanism. Kafka’s transactional producer/consumer API, for instance, achieves EOS for Kafka‑to‑Kafka pipelines by wrapping reads, writes, and offset commits in a single atomic transaction — but the underlying network hops between brokers are still, at a low level, subject to the same retry‑and‑possibly‑duplicate physics as plain at‑least‑once delivery. Understanding this helps you see through marketing claims: ask “what happens if a network packet is duplicated mid‑flight?” and you’ll usually find at‑least‑once delivery quietly doing the heavy lifting underneath.
3.9 Message ordering under redelivery
A subtlety that trips up many engineers: redelivery can disturb ordering. Suppose messages A, B, and C are delivered in order, but the consumer fails to acknowledge A in time (perhaps it was just slow) while B and C are acknowledged quickly. The broker may redeliver A after B and C have already been processed, resulting in an effective processing order of B, C, A. Systems that need strict ordering guarantees alongside at‑least‑once delivery — such as Kafka (per‑partition ordering) or SQS FIFO queues (per message‑group ordering) — solve this by ensuring that only one message from a given ordering unit is “in flight” (unacknowledged) at a time, so a stuck message blocks its own successors rather than letting them race ahead.
Architecture and Components
A typical at‑least‑once delivery system is composed of the following building blocks — producer, broker, consumer, dead‑letter queue, dedup store — each with a very specific role in preventing message loss while tolerating duplicates.
4.1 Producer
The component that generates and publishes a message onto the queue or topic. Producers are usually simple — they write a message and, in most systems, wait for a broker‑side ACK that the message was durably stored (this is the producer’s own “at‑least‑once send,” often achieved with retries on the publish call itself).
4.2 Broker / Queue
The durable middleman: Kafka, RabbitMQ, Amazon SQS, Google Pub/Sub, Azure Service Bus, or ActiveMQ. It persists messages (often to disk or a replicated log), tracks which messages have been acknowledged, and manages redelivery timers.
4.3 Consumer
The component that reads and processes messages. In at‑least‑once systems, the consumer’s contract is: “pull or receive a message, do your work, and only then acknowledge — never acknowledge before you’re sure the work succeeded.”
4.4 Dead Letter Queue (DLQ)
A safety‑net queue that captures messages that failed processing repeatedly (exceeding a max‑retry count), so they don’t loop forever and can be inspected by an engineer later.
4.5 Deduplication store
A fast key‑value store (Redis, DynamoDB, a relational “processed_messages” table) that the consumer checks against to implement idempotent processing.
Java example: a minimal at‑least‑once consumer skeleton
// A simplified illustration of at-least-once consumption with manual ACK
// using a Kafka-like consumer API and an idempotency check.
public class OrderEventConsumer {
private final ProcessedMessageStore processedStore; // e.g., backed by Redis/DB
private final OrderService orderService;
public void consumeLoop(MessageQueueClient client) {
while (true) {
List<Message> batch = client.poll(Duration.ofSeconds(5));
for (Message message : batch) {
try {
if (processedStore.alreadyProcessed(message.getId())) {
// Duplicate delivery - skip safely, still ACK it
client.acknowledge(message);
continue;
}
orderService.handleOrderEvent(message.getPayload());
// Mark processed BEFORE ack to avoid race on crash between
// successful processing and the ack round-trip.
processedStore.markProcessed(message.getId());
client.acknowledge(message);
} catch (Exception ex) {
// Do NOT acknowledge - broker will redeliver after timeout
log.error("Failed to process message {}", message.getId(), ex);
}
}
}
}
}Notice the key design decision: the consumer only calls acknowledge() after processing succeeds. If an exception is thrown, the message is deliberately left un‑acknowledged so the broker will redeliver it — this is the essence of how at‑least‑once delivery is implemented at the application layer.
Internal Working
Let’s go one level deeper and look at how real brokers — Kafka, SQS, RabbitMQ, Pub/Sub, and Azure Service Bus — implement at‑least‑once delivery internally. Each takes a slightly different approach, but they all share the same “acknowledge only after processing” skeleton.
5.1 Kafka’s approach
Kafka stores messages in an append‑only, partitioned log. Each consumer (or consumer group) tracks an offset — a pointer indicating the last message position it has successfully processed. At‑least‑once delivery in Kafka is achieved by the ordering of two operations:
- Consumer polls and receives a batch of messages starting from the last committed offset.
- Consumer processes the messages (writes to a database, calls an API, etc.).
- Consumer commits the new offset back to Kafka after processing.
If the consumer crashes between step 2 and step 3, the offset was never committed, so on restart Kafka will redeliver the same batch — resulting in at‑least‑once semantics. (If instead you commit the offset before processing, you get at‑most‑once — a common beginner mistake.)
5.2 Amazon SQS’s approach
SQS uses a visibility timeout instead of offsets. When a consumer receives a message, SQS doesn’t delete it — it simply hides it from other consumers for a configured period (say, 30 seconds). If the consumer calls DeleteMessage (its version of an ACK) within that window, the message is permanently removed. If the timeout expires without a delete call, the message becomes visible again and another consumer (or the same one) can pick it up — a redelivery.
5.3 RabbitMQ’s approach
RabbitMQ uses explicit consumer acknowledgments over AMQP. Each message is delivered with a delivery tag; the consumer must send basic.ack back. If the consumer’s TCP connection drops before acking, RabbitMQ requeues the message (optionally to the front of the queue) for redelivery.
5.4 Google Cloud Pub/Sub’s approach
Pub/Sub follows a model very similar to SQS: each subscription delivers a message and starts an acknowledgment deadline timer (default 10 seconds, extendable up to 600). The consumer must call acknowledge() with the message’s ackId before that deadline elapses, or Pub/Sub will redeliver it — potentially to a different subscriber instance entirely, since Pub/Sub uses a pull‑based, load‑balanced fan‑out model across all active subscribers on a subscription.
5.5 Azure Service Bus’s approach
Azure Service Bus uses a “peek‑lock” receive mode by default (as opposed to “receive‑and‑delete,” which is at‑most‑once). Peek‑lock hands the consumer a message and a lock token, hiding the message from other receivers for a lock duration. The consumer must call Complete() on that lock token to permanently remove the message; calling Abandon() explicitly releases the lock early for immediate redelivery, while simply letting the lock expire achieves the same redelivery outcome passively.
5.6 The critical ordering rule
To get at‑least‑once semantics, always process first, acknowledge second. If you acknowledge before processing completes, and a crash happens in between, the message is gone forever from the broker’s perspective — that’s at‑most‑once, not at‑least‑once, even if you intended otherwise.
Data Flow and Lifecycle
Let’s trace the complete lifecycle of a single message through an at‑least‑once system, including the failure path — because it’s the failure path, not the happy path, that this delivery model is actually designed around.
Stage‑by‑stage breakdown
| Stage | What happens | Failure handling |
|---|---|---|
| 1. Publish | Producer sends message to broker | Producer retries on network error |
| 2. Persist | Broker writes message to durable storage (disk, replicated log) | Replication ensures durability even if one broker node dies |
| 3. Deliver | Broker pushes or consumer pulls the message | Delivery failure triggers automatic re‑delivery |
| 4. Process | Consumer executes business logic | Exceptions prevent ACK, causing redelivery |
| 5. Acknowledge | Consumer confirms successful processing | Missing ACK within timeout triggers redelivery |
| 6. Complete | Broker deletes/marks message as done | N/A — terminal state |
Beginner walkthrough example
Imagine an e‑commerce app: a customer clicks “Place Order.” The order service publishes an OrderPlaced event. The inventory service consumes it to decrement stock. If the inventory service’s server restarts (say, due to a deployment) right after decrementing stock but before acknowledging, the broker will redeliver the same OrderPlaced event. Without idempotency, stock would be decremented twice for one order — a bug. With a proper idempotency check (e.g., “have I already applied this order ID to inventory?”), the duplicate delivery is a non‑event.
Extending this example one step further makes the lifecycle concrete end to end. Suppose the inventory service also publishes its own downstream event, StockReserved, once it finishes decrementing stock. If that publish itself fails partway (say, the broker connection drops right as the event is being sent), the inventory service needs its own retry logic on the publish side — otherwise the guarantee silently breaks one hop further down the chain, even though the original OrderPlaced event was handled correctly. This is precisely why the transactional outbox pattern, covered later in this guide, treats “did my business logic run” and “did my downstream event get published” as one atomic unit rather than two independent steps that can drift out of sync during a crash.
Pros, Cons and Tradeoffs
At‑least‑once delivery earns its default status by trading a bounded amount of developer discipline for an unbounded reduction in the worst business outcome — permanently losing data. Naming both sides of that trade makes the choice easy to justify.
✓ Advantages
- No message is ever silently lost — strong durability guarantee.
- Simpler and cheaper to implement than exactly‑once delivery.
- Works naturally with retry‑based fault tolerance across the network stack.
- Well supported by nearly every major message broker out of the box.
- Composable with idempotency to approximate exactly‑once effects.
✗ Disadvantages
- Consumers must be written carefully to be idempotent, adding development complexity.
- Duplicate processing can cause subtle bugs if idempotency is forgotten (double charges, duplicate emails).
- Deduplication stores add operational overhead (extra database/cache calls).
- Message ordering can be affected by redelivery in some systems.
- Higher latency under failure scenarios due to retry waits.
At‑least‑once vs at‑most‑once vs exactly‑once
| Aspect | At‑most‑once | At‑least‑once | Exactly‑once |
|---|---|---|---|
| Message loss risk | Possible | None | None |
| Duplicate risk | None | Possible | None (by design) |
| Implementation complexity | Low | Medium | High |
| Performance overhead | Lowest | Low‑Medium | Highest |
| Typical use case | Metrics/logs where occasional loss is fine | Orders, payments, notifications | Financial ledger entries, billing |
Choose at‑least‑once whenever losing a message is unacceptable but your consumer logic can be made idempotent relatively cheaply — which is true for the vast majority of business applications. Reserve the extra cost of true exactly‑once (distributed transactions, transactional outbox with dedup) for scenarios like financial ledgers where duplicate processing is catastrophic and cannot be dedup‑guarded easily.
Performance and Scalability
At‑least‑once delivery interacts with performance in several important ways — and the biggest surprise for most teams is that the bottleneck is usually the dedup layer, not the broker.
8.1 Throughput impact
Because consumers must persist idempotency keys (to a database or cache) before or after processing, at‑least‑once systems incur extra I/O per message compared to a naive “fire and forget” system. In high‑throughput pipelines (Kafka clusters processing millions of events/second), this dedup‑store lookup is often the actual bottleneck, not the message broker itself.
8.2 Batching for efficiency
Most production systems batch ACKs and dedup‑checks. Instead of checking a dedup store per message, a consumer might buffer 500 messages, deduplicate them in a single bulk read against Redis (using MGET), process the unique ones, and then commit a single batch offset — dramatically reducing per‑message overhead.
8.3 Scaling consumers horizontally
At‑least‑once systems scale well horizontally. In Kafka, adding more consumer instances to a consumer group lets partitions be spread across them, increasing parallel throughput linearly (up to the partition count). The same redelivery‑on‑failure mechanism works uniformly whether you have 1 or 1,000 consumer instances.
8.4 Retry storms
A common scalability hazard: if a downstream dependency (say, a payment gateway) goes down, every consumer retry for every in‑flight message fails simultaneously, and the resulting flood of redeliveries can itself overwhelm the system once the dependency recovers. Production systems mitigate this with exponential backoff with jitter on redelivery/retry intervals.
Java example: idempotent processing with batched dedup check
public void processBatch(List<Message> messages) {
List<String> ids = messages.stream().map(Message::getId).toList();
// Single round-trip bulk check instead of N separate calls
Set<String> alreadyProcessed = dedupStore.bulkCheck(ids);
List<Message> toProcess = messages.stream()
.filter(m -> !alreadyProcessed.contains(m.getId()))
.toList();
for (Message m : toProcess) {
orderService.handle(m.getPayload());
}
dedupStore.bulkMarkProcessed(
toProcess.stream().map(Message::getId).toList()
);
queueClient.acknowledgeAll(messages); // ack the whole batch at once
}High Availability and Reliability
At‑least‑once delivery is, at its core, a reliability feature — but the broker delivering that guarantee must itself be highly available, or the guarantee is meaningless.
9.1 Replication
Production brokers replicate every message across multiple nodes before acknowledging the producer. Kafka, for example, uses a leader‑follower replication model per partition: a message is only considered “committed” once it has been written to the leader and replicated to a configurable number of in‑sync replicas (min.insync.replicas). This ensures that even if the leader broker crashes immediately after accepting a message, the message survives on a follower and the guarantee holds.
9.2 Failover
When a broker node fails, a replica is promoted to leader (via a consensus mechanism such as ZooKeeper/KRaft in Kafka, or Raft in newer systems). Producers and consumers reconnect to the new leader automatically. During this failover window, some redelivery or brief unavailability may occur — but no committed message is lost.
9.3 Disaster recovery
For cross‑region resilience, systems like Kafka’s MirrorMaker or cloud‑native replication (SQS cross‑region replication, Pub/Sub’s global routing) asynchronously copy messages to a secondary region so that a full regional outage doesn’t cause data loss — though this typically weakens ordering guarantees and adds replication lag.
At‑least‑once delivery guarantees no message loss within the broker’s durability boundary. If a producer sends a message and crashes before receiving a durability ACK from the broker, and the message never made it to disk, that message can still be lost — the guarantee only kicks in once the broker has durably accepted it. This is why producers themselves must retry publishes with idempotent producer IDs (as Kafka’s idempotent producer feature does) to avoid duplicate publishes on retry.
9.4 Consumer‑side reliability patterns
- Circuit breakers around downstream calls so a failing dependency doesn’t cause every consumer thread to hang and time out simultaneously.
- Graceful shutdown hooks that stop pulling new messages but finish in‑flight processing and ACK before the process exits during a deploy.
- Health checks that remove unhealthy consumer instances from the group before they can hold partitions hostage.
9.5 Quorum‑based durability
Many at‑least‑once systems use a quorum write model: a message is only considered durably committed once it has been acknowledged by a majority (or a configured minimum) of replica nodes, not just the leader. This protects against the classic failure scenario where a leader accepts a write, crashes before replicating it, and a follower is promoted to leader without ever having seen that message — which would silently violate the at‑least‑once guarantee. Kafka’s acks=all producer setting combined with min.insync.replicas=2 is the canonical example: the producer only considers a publish successful once at least two replicas confirm the write.
9.6 Testing reliability in practice
Mature teams treat at‑least‑once guarantees as something to be continuously verified, not just configured once and trusted forever. Chaos‑engineering practices — deliberately killing broker nodes, injecting network partitions, or force‑restarting consumer pods mid‑batch — are the only reliable way to prove that redelivery and idempotency actually behave as designed under real failure conditions, rather than only in the happy path that unit tests typically cover.
Security
Security in at‑least‑once messaging systems spans transport, authentication, and message‑level integrity — and duplicated messages introduce their own specific class of security concern that doesn’t exist in at‑most‑once systems.
10.1 Transport security
Message brokers should be configured with TLS/SSL for all producer‑broker and broker‑consumer traffic to prevent eavesdropping or tampering with messages (and their ACKs) in transit.
10.2 Authentication and authorisation
Modern brokers support SASL/OAuth‑based authentication (Kafka), IAM policies (Amazon SQS), and role‑based access control to ensure only authorized producers can publish to a topic and only authorized consumer groups can subscribe.
10.3 Duplicate‑related security risks
Because at‑least‑once systems can redeliver messages, security‑sensitive operations (like “send a password reset email” or “issue a refund”) must specifically guard against duplicate side effects — a naive re‑processing of a duplicated “IssueRefund” event could be exploited or simply cause financial loss if idempotency isn’t enforced.
Treat every message ID as a security‑relevant idempotency key, not just a performance optimisation. For payment or account‑mutation events, combine idempotency keys with database‑level unique constraints (e.g., a unique constraint on (order_id, event_type)) so that even a bug in application‑level dedup logic cannot cause a double‑charge — the database itself becomes the last line of defense.
10.4 Message‑level integrity
Signing messages (HMAC or digital signatures) ensures that even if a message is redelivered or intercepted, its payload cannot be tampered with between producer and consumer.
10.5 Replay attacks vs legitimate redelivery
It’s worth distinguishing between two things that look superficially similar: a broker’s own legitimate redelivery mechanism, and a malicious actor capturing and replaying an old message to trigger an action twice (a classic replay attack). Idempotency keys and dedup checks defend against both simultaneously, but for externally‑facing endpoints (like payment webhooks), teams typically add an additional timestamp or nonce check — rejecting messages whose timestamp is older than a reasonable window — specifically to reduce the attack surface for an adversary who has somehow captured a valid, signed message and is trying to resubmit it long after the fact.
10.6 Least‑privilege access to Dead Letter Queues
DLQs often accumulate sensitive payloads that failed processing (which may include personally identifiable information from a failed order or payment event). Access to DLQs should be restricted with the same rigor as production databases, and automated tooling that reprocesses DLQ messages should itself go through the same authentication and idempotency checks as the original pipeline.
Monitoring, Logging and Metrics
Operating an at‑least‑once system in production requires visibility into a specific set of signals — the ones that quietly foreshadow trouble before customers notice it.
| Metric | What it tells you |
|---|---|
| Consumer lag | How far behind the consumer is from the latest published message — rising lag signals a slow or stuck consumer |
| Redelivery / retry count | How often messages are being redelivered — spikes indicate consumer failures or slow processing |
| Dead Letter Queue depth | Number of messages that permanently failed processing — should normally be near zero |
| Duplicate detection rate | Percentage of messages your dedup layer identifies as already‑processed — helps quantify real‑world duplication |
| ACK latency | Time between message delivery and acknowledgment — high latency risks premature timeout‑triggered redelivery |
| End‑to‑end processing latency | Time from publish to successful ACK — key SLA metric |
11.1 Logging practices
Every message should carry a unique, traceable ID (often a UUID or a composite business key) that gets logged at every stage: publish, receive, process‑start, process‑end, ACK. This allows engineers to reconstruct a message’s full journey when debugging — especially crucial for diagnosing why a message was redelivered multiple times.
11.2 Correlation IDs
In microservice architectures, a correlation ID travels with a message across every hop, letting you stitch together logs from the producer, broker, and every downstream consumer in a distributed trace (using tools like Jaeger, Zipkin, or OpenTelemetry).
11.3 Alerting
Production teams typically alert on: DLQ depth exceeding a threshold, consumer lag growing continuously over N minutes, and redelivery rate exceeding a baseline percentage — each of these is an early warning sign of a downstream outage or a bug in consumer logic.
11.4 Building an operational dashboard
A well‑designed messaging dashboard typically groups these signals into three panels: a health panel (consumer lag per partition, active consumer count, broker node status), a throughput panel (messages published per second, messages consumed per second, redelivery rate as a percentage of total deliveries), and a failure panel (DLQ depth over time, top error types from failed processing, oldest un‑acknowledged message age). Teams that invest in this visibility upfront tend to catch idempotency bugs and downstream outages within minutes rather than discovering them days later through a customer complaint about a duplicate charge or a missing order confirmation.
Deployment and Cloud
At‑least‑once delivery is available as a managed feature across all major cloud providers, removing the operational burden of running your own broker cluster.
| Cloud Service | Delivery Model | Key Mechanism |
|---|---|---|
| Amazon SQS (Standard) | At‑least‑once | Visibility timeout + DeleteMessage |
| Amazon SQS (FIFO) | Exactly‑once processing (within limits) | Deduplication ID + sequencing |
| Google Cloud Pub/Sub | At‑least‑once (exactly‑once mode available) | Ack deadline + ack ID |
| Azure Service Bus | At‑least‑once | Peek‑lock + Complete |
| Apache Kafka (self‑managed or MSK/Confluent Cloud) | Configurable — at‑least‑once by default | Consumer offsets + commit timing |
| RabbitMQ (self‑managed or CloudAMQP) | At‑least‑once | Manual ACK / requeue |
12.1 Kubernetes considerations
When consumers run as Kubernetes pods, at‑least‑once semantics interact directly with pod lifecycle events. A rolling deployment that sends SIGTERM to a pod mid‑processing must be handled with a graceful shutdown period (terminationGracePeriodSeconds) long enough for in‑flight messages to finish processing and ACK — otherwise every deployment becomes a mini redelivery storm.
12.2 Infrastructure‑as‑Code example (conceptual)
// Conceptual SQS queue configuration highlighting at-least-once relevant settings
Queue "order-events" {
visibility_timeout = 30s // how long a message stays hidden after being received
message_retention = 4d // how long undelivered messages persist
max_receive_count = 5 // retries before moving to DLQ
dead_letter_queue = "order-events-dlq"
}12.3 Serverless considerations
In serverless consumers (AWS Lambda triggered by SQS, Cloud Functions triggered by Pub/Sub), the platform automatically retries the function invocation if it errors or times out — meaning at‑least‑once semantics are baked into the trigger model itself, and idempotency remains entirely the developer’s responsibility inside the function body.
12.4 Choosing a managed service vs self‑hosting
Teams frequently ask whether to self‑host a broker like Kafka or RabbitMQ, or use a managed cloud service like SQS, Pub/Sub, or Confluent Cloud. From an at‑least‑once delivery standpoint specifically, managed services remove an entire category of operational risk: replication factor, quorum settings, and failover automation are handled by the provider, and the delivery guarantee is backed by a published SLA. Self‑hosting gives you finer control over tuning (batch sizes, retention, partition counts) and can be more cost‑effective at very large, steady‑state scale, but it shifts the burden of correctly configuring durability and failover onto your own operations team — a subtle misconfiguration (like an under‑replicated topic or a too‑short visibility timeout) can silently weaken the guarantee you think you have.
12.5 Cost considerations across environments
Cost scales primarily with message volume, retention period, and the number of redeliveries your system generates under failure. A pipeline experiencing frequent downstream outages will pay for many more redeliveries (and therefore more compute and network) than a healthy one — which is another reason the reliability metrics discussed in the monitoring section aren’t just about correctness, but directly affect your cloud bill as well.
Databases, Caching and Load Balancing
Databases and caches are where the abstract idea of “idempotent consumer” becomes concrete rows and keys — and getting the shape of that data right is what separates a system that theoretically supports at‑least‑once delivery from one that actually survives production.
13.1 The deduplication table pattern
The most common database pattern for supporting at‑least‑once consumers is a dedicated table that records processed message IDs:
CREATE TABLE processed_messages (
message_id VARCHAR(64) PRIMARY KEY,
processed_at TIMESTAMP NOT NULL DEFAULT now(),
event_type VARCHAR(64)
);
-- Idempotent insert pattern (works across most relational databases)
INSERT INTO processed_messages (message_id, event_type)
VALUES ('evt-98213', 'OrderPlaced')
ON CONFLICT (message_id) DO NOTHING;The consumer performs its business logic and this insert within the same database transaction whenever possible, so that either both the business effect and the dedup record are committed together, or neither is — preventing a crash from leaving the system in a half‑processed state.
13.2 The transactional outbox pattern
On the producer side, a related and equally important pattern ensures messages are reliably published in the first place: the service writes its business change and the outgoing event to the same local database transaction (an “outbox” table), and a separate relay process reads from the outbox and publishes to the broker with retries — guaranteeing at‑least‑once publishing without losing events if the service crashes right after committing the business change.
13.3 Caching for fast deduplication
For very high‑throughput systems, checking a relational database for every message is too slow. Instead, teams use Redis with a TTL‑based key (e.g., SETNX dedup:evt-98213 1 EX 86400) as a fast, approximate dedup layer, falling back to the database as the source of truth for critical operations like payments.
13.4 Load balancing consumers
Consumer groups are themselves a form of load balancing: the broker (or client library) distributes partitions or messages across available consumer instances, automatically rebalancing when instances join or leave — this rebalancing itself can trigger redeliveries for in‑flight messages whose processing hadn’t been acknowledged yet, another reason idempotency is non‑negotiable.
13.5 Sizing the dedup store
A practical question every team faces: how long must a dedup record be retained before it’s safe to expire it? The answer depends on the maximum plausible redelivery window of your broker — if your queue’s message retention is four days, a dedup key that expires after one hour creates a gap where a very late redelivery could slip through undetected and be processed twice. As a rule of thumb, dedup record TTLs should be set to at least match, and ideally exceed, the broker’s maximum message retention or redrive period, even though this means the dedup store will hold more data than a naive implementation might assume.
APIs and Microservices
The at‑least‑once problem isn’t confined to message queues — it also appears in synchronous REST APIs whenever a client retries a request after a timeout (not knowing if the server actually processed it).
14.1 Idempotency keys in REST APIs
The standard solution, used by Stripe, PayPal, and most payment APIs, is an Idempotency‑Key header:
POST /api/payments HTTP/1.1
Idempotency-Key: 6a1f9e2b-9c3d-4e11-8b2a-1d9f7c4a55e0
Content-Type: application/json
{
"amount": 4999,
"currency": "INR",
"orderId": "ORD-88213"
}The server stores the key alongside the response the first time it processes the request; if the same key arrives again (a client retry), it returns the cached response instead of re‑executing the payment — turning a synchronous at‑least‑once retry into an exactly‑once effect.
14.2 Java example: idempotency key filter in a Spring Boot service
@RestController
@RequestMapping("/api/payments")
public class PaymentController {
private final IdempotencyKeyStore keyStore;
private final PaymentService paymentService;
@PostMapping
public ResponseEntity<PaymentResponse> createPayment(
@RequestHeader("Idempotency-Key") String idempotencyKey,
@RequestBody PaymentRequest request) {
Optional<PaymentResponse> cached = keyStore.getCachedResponse(idempotencyKey);
if (cached.isPresent()) {
return ResponseEntity.ok(cached.get()); // safe retry, no double charge
}
PaymentResponse response = paymentService.process(request);
keyStore.storeResponse(idempotencyKey, response);
return ResponseEntity.ok(response);
}
}14.3 Event‑driven microservices
In a microservices architecture built around events (order service → inventory service → shipping service → notification service), at‑least‑once delivery is usually the backbone guarantee for every hop, because it lets each service scale, deploy, and fail independently while still guaranteeing the overall workflow eventually completes — a property closely related to eventual consistency and the Saga pattern for distributed transactions.
14.4 gRPC and streaming APIs
Even bidirectional streaming protocols like gRPC streams need explicit application‑level acknowledgment logic if you want at‑least‑once guarantees, since the underlying HTTP/2 stream reconnecting after a network blip doesn’t automatically replay unacknowledged messages the way a purpose‑built message broker does.
Design Patterns and Anti‑patterns
Certain patterns show up again and again in codebases that get at‑least‑once delivery right, and certain anti‑patterns show up just as reliably in codebases that get it wrong. Naming both makes them easier to spot in code review.
15.1 Recommended patterns
Track processed IDs
Track processed message IDs and skip duplicates. The foundational pattern for any at‑least‑once system.
Atomic publish
Write business state and outgoing events in one local transaction to guarantee reliable publishing.
Bounded failure lane
Redirect repeatedly‑failing messages to a separate queue for manual inspection instead of looping forever.
Avoid retry storms
Space out redelivery attempts to avoid overwhelming a recovering downstream dependency.
Safe API retries
A client‑supplied unique token that lets a server safely respond identically to a retried synchronous request.
Multi‑service workflow
Coordinates a sequence of local transactions across services using at‑least‑once events, with compensating actions to undo partial work if a later step fails.
These patterns are rarely used in isolation. A typical production‑grade order‑processing pipeline combines a transactional outbox on the producer side, at‑least‑once delivery through Kafka or SQS as the transport, an idempotent consumer with a deduplication table on the receiving side, and a dead letter queue as the final safety net — each pattern plugging a specific gap left by the others. Understanding at‑least‑once delivery in isolation is useful, but understanding how it composes with these surrounding patterns is what actually lets you build systems that survive real production failures.
15.2 Anti‑patterns to avoid
✗ Common Mistakes
- Acknowledging before processing — silently converts your system to at‑most‑once.
- Assuming message order is preserved across redeliveries — many brokers don’t guarantee this once retries are involved.
- Non‑idempotent side effects — e.g., calling a third‑party API (like sending an SMS) without a dedup check.
- Infinite retry with no DLQ — a permanently broken message blocks the entire partition/queue forever.
- Relying purely on in‑memory dedup caches — lost on consumer restart, causing duplicate processing right when it matters most (during recovery).
✓ Better Alternatives
- Process fully, then ACK — always in that order.
- Design consumers to be order‑tolerant, or use partition keys for ordering guarantees where needed.
- Wrap external calls with idempotency keys or dedup checks before calling.
- Always configure a DLQ with a bounded max‑retry count.
- Persist dedup state durably (database or Redis with proper TTL), not just in local memory.
Best Practices and Common Mistakes
A handful of simple habits, applied consistently, prevent the vast majority of real‑world at‑least‑once bugs — almost all of which live in application code, not in the broker itself.
- Always design consumers to be idempotent from day one — retrofitting idempotency after a production incident is far more painful than building it in up front.
- Use a unique, stable message ID generated by the producer (not the broker) so the same logical event always has the same ID even across retried publishes.
- Combine dedup checks with database unique constraints for financially sensitive operations as a defense‑in‑depth measure.
- Set sane visibility/ACK timeouts — too short causes unnecessary redeliveries of messages that are still legitimately processing; too long delays recovery after a real consumer crash.
- Monitor DLQ depth and consumer lag continuously — these two metrics catch the majority of at‑least‑once related production issues.
- Test failure scenarios explicitly — deliberately kill a consumer mid‑processing in staging to verify redelivery and idempotency actually work as designed.
- Document the delivery guarantee at every service boundary so downstream teams know whether to expect duplicates.
A frequent bug: teams add a dedup table but forget to wrap the business write and the dedup insert in the same transaction. A crash between the two leaves the system thinking a message was processed when it wasn’t (or vice versa) — always make these two writes atomic together.
Real‑World Industry Examples
The best way to internalise at‑least‑once delivery is to see the companies that built their business on it and the design decisions they made around it.
Netflix
Netflix’s event‑driven architecture, built heavily on Kafka, uses at‑least‑once delivery for its viewing‑activity and recommendation pipelines. Downstream consumers that update “continue watching” state and personalisation models are designed to be idempotent, since Netflix’s massive scale makes occasional redelivery a statistical certainty rather than an edge case.
Amazon
Amazon SQS, which powers huge parts of Amazon’s own order‑processing and fulfillment systems, was explicitly designed around at‑least‑once delivery as its Standard Queue default, because Amazon’s engineers determined decades ago that losing an order‑processing message was categorically worse than occasionally processing one twice — as long as the fulfillment logic was idempotent.
Uber
As mentioned earlier, Uber’s trip and payment event pipelines rely on Kafka’s at‑least‑once guarantees combined with idempotency keys tied to trip IDs and payment IDs, ensuring that network blips during a ride never silently drop a fare‑relevant event.
Banking and payments industry
Payment processors like Stripe popularised the Idempotency‑Key HTTP header pattern precisely because their API sits in the at‑least‑once world by necessity — clients retry timed‑out requests, and Stripe must guarantee a retried “charge card” request never results in a double charge.
IoT and telemetry
Companies operating fleets of IoT sensors (smart meters, industrial equipment monitors) typically choose at‑least‑once delivery for telemetry ingestion pipelines, since losing a sensor reading (an at‑most‑once failure mode) is often worse for downstream analytics and safety systems than occasionally double‑counting one reading, which can be filtered out during aggregation.
LinkedIn built Apache Kafka in the first place to solve its own internal activity‑tracking and log‑aggregation problems at massive scale, and Kafka’s default delivery semantics were at‑least‑once from the very beginning — a design decision that has since propagated to thousands of companies that adopted Kafka as their central nervous system for event‑driven data.
Slack
Slack’s message‑delivery infrastructure, which fans a single sent message out to every connected client across a user’s devices, is built to tolerate redelivery — client applications deduplicate incoming messages by message ID so that a brief WebSocket reconnect never results in a message appearing twice in your chat window, even though the underlying delivery pipe is at‑least‑once.
PayPal
PayPal’s webhook‑notification system, which informs merchant servers about payment events, explicitly documents that webhooks may be delivered more than once and instructs integrators to use the event ID for deduplication — a textbook example of an at‑least‑once API contract being surfaced directly to external developers rather than hidden away.
FAQ, Summary and Key Takeaways
A short round of common questions, followed by a plain‑English summary and the takeaways worth writing down for later.
Is at‑least‑once delivery the same as “reliable messaging”?
It’s one specific, well‑defined form of reliable messaging — the one that prioritizes “never lose a message” over “never duplicate a message.” Reliable messaging is the broader goal; at‑least‑once is one of the concrete guarantees used to achieve it.
Can I get exactly‑once delivery instead?
True exactly‑once delivery across an unreliable network is theoretically impossible in the general case. What production systems call “exactly‑once” is almost always at‑least‑once delivery combined with idempotent processing or deduplication, which produces exactly‑once effects for the application, even though the underlying transport may still redeliver.
Does at‑least‑once delivery guarantee message ordering?
Not inherently. Ordering and delivery guarantees are separate concerns. Kafka provides ordering per‑partition, and SQS FIFO queues provide ordering within a message group, but Standard SQS and many other at‑least‑once systems do not guarantee strict ordering, especially once retries are involved.
How do I know if my system needs at‑least‑once delivery?
Ask: “Is it worse to lose this piece of data, or to occasionally process it twice?” If losing it is worse (orders, payments, critical notifications, audit logs), choose at‑least‑once and build idempotent consumers. If occasional loss is acceptable and duplicates are hard to filter (some high‑volume metrics/logging use cases), at‑most‑once may be simpler and cheaper.
What’s the simplest way to add idempotency to an existing consumer?
Add a unique message ID to every event, create a small “processed_messages” table or Redis key set, and check‑and‑record that ID as the very first and very last steps of your processing logic, ideally within the same transaction as your main business write.
Does at‑least‑once delivery cost more money to run?
Usually a little, yes. The extra cost comes from durability (replicated storage), redelivery traffic during failures, and the dedup‑store lookups your consumers perform. In practice this overhead is small compared to the cost of the data‑loss incidents it prevents, which is why it remains the default choice for production systems even though it’s not the theoretically cheapest option.
Is at‑least‑once delivery only relevant to message queues?
No. The same underlying idea — “retry until confirmed, so the receiver must tolerate duplicates” — shows up in HTTP client retries, webhook delivery systems, mobile push notifications, database replication, and even TCP itself at a lower layer. Once you learn to recognize the pattern, you’ll see it across almost every corner of distributed systems design.
Summary
At‑least‑once delivery is a small, opinionated promise: your message will arrive at its destination, and it may occasionally arrive more than once, but it will never silently vanish. That single promise, made cheaply by nearly every modern message broker and honestly acknowledged by nearly every serious API, is what quietly holds together the world’s payment systems, order pipelines, notification fabrics, and event‑driven microservices. Everything else in this guide — ACKs, retries, dedup tables, idempotency keys, DLQs, transactional outboxes, sagas — is engineering scaffolding around that one promise, designed to make its unavoidable duplicates invisible to the end user.
Key Takeaways
- At‑least‑once delivery guarantees a message will reach the consumer one or more times — it will never be silently lost, but it may occasionally be duplicated.
- It is achieved through the combination of durable broker storage, acknowledgments, and redelivery on timeout.
- The golden implementation rule is: process first, acknowledge second — never the other way around.
- Idempotency at the consumer (or API) level is what makes at‑least‑once delivery safe to build production systems on.
- Common infrastructure patterns supporting it include the transactional outbox, deduplication tables, idempotency keys, and dead letter queues.
- Virtually every major message broker — Kafka, SQS, RabbitMQ, Pub/Sub, Azure Service Bus — defaults to or supports at‑least‑once semantics, making it the pragmatic industry standard.
- Companies like Amazon, Netflix, Uber, and Stripe all rely on at‑least‑once delivery at massive scale, paired with careful idempotent design, rather than chasing theoretically‑impossible perfect exactly‑once transport guarantees.
Once at‑least‑once feels solid, the natural next topics to study are the Transactional Outbox pattern, the Saga pattern for distributed workflows, and Exactly‑Once Semantics (EOS) in Kafka — each of which builds directly on the foundation you have just learned here.