22. Message Queues
22.4 How do acks and visibility timeouts work?
Reliability
At the bank: When a clerk calls your token, it's taken off the board so no other clerk calls it. If the clerk goes on a break and never finishes, after a while the manager puts the token back on the board for someone else.
In the system: When a consumer receives a message, the broker doesn't delete it; it hides it from other consumers:
- Receive: the message becomes invisible (in flight) for a visibility timeout, 30 seconds by default in SQS (up to 12 hours).
- Ack (delete) within the timeout: the message is gone for good.
- No ack in time (the worker crashed or is too slow): the message becomes visible again and another consumer gets it.
Figure: an unacknowledged message always comes back, which is what makes at-least-once work.
In the chapter's code, a message received at second 0 is invisible at second 10, and reappears at second 31 when the worker never acked it.
RabbitMQ does the same with unacked messages: if the consumer's connection closes, they're requeued. Kafka works differently: the consumer commits an offset ("I've processed up to message 4,812"); after a crash, the next consumer restarts from the last committed offset.
Getting the timeout right:
- Longer than the slowest normal job. If processing can take 45 seconds, a 30-second timeout guarantees duplicates.
- Extend it while working for long jobs (SQS lets you change a message's visibility; many libraries send heartbeats).
- Not too long, or a crashed worker's messages sit hidden for ages before retrying.
- Prefetch limits (RabbitMQ
prefetch, SQS batch size): don't let one worker grab 500 messages it can't finish in time.
Memory code: "Taken off the board while you work; put back if you don't finish."