Skip to content

At-Most-Once Delivery — Complete Guide

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about At. We cover key concepts, practical examples, and best practices to help you master this topic.

At-most-once delivery delivers each message once or not at all, accepting message loss for maximum throughput. Used for non-critical data like metrics and logs.

What You'll Learn

By the end of this lesson, you will understand at-most-once delivery, when it is appropriate, how to implement it, and the scenarios where losing messages is acceptable.

Why It Matters

Not all data needs guaranteed delivery. Server metrics, debug logs, and real-time analytics can tolerate occasional message loss. At-most-once delivers the highest throughput because the broker does not wait for acknowledgments or persist messages.

Real-World Use

A server monitoring system sends CPU and memory metrics every 10 seconds. If one metric message is lost, the next one arrives 10 seconds later. The dashboard shows a 10-second gap instead of a spike. The operations team can tolerate this loss.

At-Most-Once Flow

sequenceDiagram
    participant P as Producer
    participant B as Broker
    participant C as Consumer

    P->>B: Publish message
    B->>C: Deliver message (auto-ack)
    C->>C: Process message
    Note over C: Consumer crashes
    Note over P: Message is lost

The broker removes the message from the queue as soon as it is delivered. If the consumer crashes, the message is gone. No redelivery.

Implementing At-Most-Once in RabbitMQ

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.queue_declare(queue='metrics')

channel.basic_publish(
    exchange='',
    routing_key='metrics',
    body='cpu=45,memory=62',
    properties=pika.BasicProperties(delivery_mode=1)
)
print("Published metric (non-persistent)")
connection.close()

The message is non-persistent (delivery_mode=1). The queue is not durable. Both queue and messages disappear on restart.

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.queue_declare(queue='metrics')

def callback(ch, method, properties, body):
    print(f"Metric: {body.decode()}")
    # No ack needed — auto_ack handles it

channel.basic_consume(
    queue='metrics',
    on_message_callback=callback,
    auto_ack=True
)
print("Listening for metrics...")
channel.start_consuming()

Expected output:

Listening for metrics...
Metric: cpu=45,memory=62

With auto_ack=True, the broker removes the message immediately upon delivery. No acknowledgment is sent back.

When to Use At-Most-Once

Use Case Acceptable Loss? Why
Server metrics Yes Next value arrives in seconds
Debug logs Yes Recent logs matter, not every event
Real-time analytics Sometimes Aggregations tolerate small gaps
Heartbeat/ping Yes Next heartbeat proves liveness
User notifications No Missing notification = bad UX
Payment events No Missing payment = lost revenue

Throughput Comparison

import pika
import time

def benchmark_at_most_once(count=50000):
    conn = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
    ch = conn.channel()
    ch.queue_declare(queue='perf', durable=False)

    start = time.time()
    for i in range(count):
        ch.basic_publish(
            exchange='', routing_key='perf',
            body=f'msg-{i}',
            properties=pika.BasicProperties(delivery_mode=1)
        )
    elapsed = time.time() - start
    print(f"At-most-once: {count} msgs in {elapsed:.2f}s ({count/elapsed:.0f} msg/s)")
    conn.close()

benchmark_at_most_once()

Expected output:

At-most-once: 50000 msgs in 1.80s (27778 msg/s)

At-most-once is significantly faster than at-least-once with persistence because no disk writes or acknowledgment wait times are involved.

Tradeoffs of At-Most-Once

Think of at-most-once like a whiteboard. You write a message, someone reads it, and you erase it immediately. If the reader was distracted and missed it, the message is gone forever.

The benefit is speed. No handshakes, no confirmations, no disk I/O. The cost is reliability. Messages can be lost at any point — during network transmission, during broker processing, or during consumer handling.

Common Mistakes

1. Using At-Most-Once for Critical Data

Never use at-most-once for payment instructions, order confirmations, or email notifications. These must use at-least-once with idempotent consumers to prevent message loss.

2. Assuming At-Most-Once Guarantees No Duplicates

At-most-once does not guarantee no duplicates. Network retries can cause the same message to be sent twice. The guarantee is "at most once is processed," not "at most once is sent."

3. Ignoring Broker Crashes

If the broker crashes with at-most-once, all in-memory messages are lost. If you restart the broker, the queue is recreated empty. Any messages that were in flight are gone.

4. Not Monitoring Loss Rate

At-most-once systems should monitor delivery success rates. If 5% of messages are lost (e.g., due to network issues), the system is unreliable even for metrics. Set alerts on loss rates.

5. Mixing Guarantees in the Same Queue

If some messages need at-least-once and others can tolerate at-most-once, use separate queues. Different consumers on the same queue cannot use different ack modes safely.

Practice Questions

1. What is the key difference between at-most-once and at-least-once?

At-most-once accepts message loss. The broker removes the message on delivery. At-least-once prevents loss by redelivering unacknowledged messages, but allows duplicates.

2. When is at-most-once appropriate?

For non-critical, high-volume data where occasional loss is acceptable. Examples: metrics, logs, heartbeats, real-time dashboards, and analytics events.

3. How do you implement at-most-once in RabbitMQ?

Set auto_ack=True on the consumer and use non-persistent messages (delivery_mode=1). The queue should not be durable if restart loss is acceptable.

4. Can at-most-once still produce duplicates?

Yes. Network retries at the producer side can send the same message twice. The broker may process both. At-most-once only means the broker will not redeliver after consumer failure.

Challenge

Design a metrics collection system that sends 100,000 data points per second from 10,000 servers. The dashboard tolerates 1% data loss. Compare at-most-once, at-least-once, and exactly-once approaches. Justify your choice.

FAQ

Is at-most-once the same as 'fire and forget'?

Yes. Fire-and-forget means the producer sends the message and immediately forgets about it. At-most-once adds that the consumer will process it at most once, possibly zero times.

Can Kafka do at-most-once?

Yes. In Kafka, set enable.auto.commit=true and commit offsets before processing messages. If the consumer crashes after committing but before processing, messages are skipped.

Does SQS support at-most-once?

SQS defaults to at-least-once. For at-most-once, use a very short visibility timeout (1 second). The message may be re-delivered within that window, so it is not pure at-most-once.

What is the performance gain of at-most-once?

2-5x faster than at-least-once with persistence. The exact gain depends on disk speed, message size, and network latency.

Can I combine at-most-once with a dead-letter queue?

Not effectively. Since messages are removed on delivery, there is nothing to dead-letter. At-most-once systems are designed to accept loss, not to recover lost messages.

Mini Project: Metrics Pipeline

import pika
import json
import random
import time

def publish_metrics():
    conn = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
    ch = conn.channel()
    ch.queue_declare(queue='metrics', durable=False)

    for _ in range(20):
        metric = {
            'cpu': round(random.uniform(10, 90), 1),
            'memory': round(random.uniform(30, 80), 1),
            'disk': round(random.uniform(20, 95), 1),
            'timestamp': time.time()
        }
        ch.basic_publish(
            exchange='', routing_key='metrics',
            body=json.dumps(metric),
            properties=pika.BasicProperties(delivery_mode=1)
        )
        time.sleep(0.1)

    ch.close()
    conn.close()

publish_metrics()
print("Published 20 metrics (fire-and-forget)")

Expected output:

Published 20 metrics (fire-and-forget)

What's Next

Now that you understand at-most-once, compare it with at-least-once and learn about exactly-once delivery for the strongest guarantee, then explore dead letter queues for handling failed messages.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro