Lesson 4 of 18
22. Message Queues

22.3 What are the delivery guarantees?

3 min read+10 XP

At the bank: A clerk can clear your token before finishing your job (if they then faint, your job is forgotten) or after finishing (if they faint just before clearing it, another clerk calls your number again and does the job a second time). There's no third option where fainting is free.

In the system: A worker can crash at any moment. When it acknowledges the message decides what a crash costs:

ack before processing loses messages; ack after processing can repeat them; idempotency makes repeats harmless.

Figure: choose between "maybe lost" and "maybe twice", then make "twice" harmless.

22.3.1 At-most-once

At the bank: The token is cleared the moment you're called. If the clerk faints, your job is simply gone.

In the system: Ack first, then process. Each message is processed zero or one times. A crash after the ack loses the message. In the chapter's code, 100 messages with two crashes leave 98 processed.

Fine for data where a gap doesn't matter: metrics samples, "typing..." indicators, some logs.

22.3.2 At-least-once

At the bank: The token is cleared only when the job is done. If the clerk faints just before clearing it, your number is called again and the job is done twice.

In the system: Process first, then ack. If the worker crashes before acking, the broker redelivers the message. Nothing is lost, but some messages are processed more than once. In the chapter's code, the same two crashes give 102 processings for 100 messages: two done twice.

This is the default in SQS standard queues, RabbitMQ with manual acks, and most Kafka consumers. Duplicates also happen for other reasons: a producer retrying a send after a timeout, or a visibility timeout expiring while a slow worker is still busy (the Level 4 bug).

22.3.3 Exactly-once (and why it's mostly "effectively once")

At the bank: The bank can't stop a fainting clerk. What it can do is stamp the passbook when money is paid, so the second clerk sees the stamp and doesn't pay again. The job may be attempted twice; the effect happens once.

In the system: True exactly-once delivery across a network isn't something you can guarantee in general: a crash between "did the work" and "recorded that I did it" always leaves a gap. What systems offer is exactly-once effect, in limited scopes:

  • SQS FIFO queues remove duplicate sends within a 5-minute deduplication window, using a deduplication ID.
  • Kafka offers idempotent producers and transactions, so a "read, process, write back to Kafka" pipeline can commit its output and its position together.

Neither makes your database update and the ack one atomic step. So the practical recipe is: at-least-once delivery + idempotent consumers (22.6). That's what "exactly-once" means in almost every real system.

Memory code: "Ack first and maybe lose it; ack last and maybe repeat it; make repeats harmless."