Requirements and the Single-Server Limit
In one line: the single-server queue is not a strawman. It is what a queue is in one process, and understanding exactly which four properties break when you distribute it gives you the agenda for the whole design.
Functional requirements
Clients must be able to:
| Operation | Detail |
|---|---|
| Queue creation | Create a queue and define parameters such as queue name, queue size, and maximum message size |
| Send message | Producers send messages to a designated queue |
| Receive message | Consumers retrieve messages from their designated queues |
| Delete message | Consumers delete a message from the queue after successfully processing it |
| Queue deletion | Clients delete a specific queue |
Notice that delete is separate from receive
This is the most consequential detail in the list, and it is easy to read past.
Receiving a message does not remove it. The consumer must explicitly delete it after successfully processing it. That two-step protocol is what makes at-least-once delivery possible: if the consumer dies between receiving and deleting, the message is still there for someone else.
If receive-and-remove were one atomic step, a consumer crash would lose the message permanently. The API is shaped this way on purpose, and Lesson 9 shows the two mechanisms — offset tracking and visibility timeout — built on top of it.
Non-functional requirements
| Requirement | Detail |
|---|---|
| Durability | Data must persist once received. Since producers and consumers fail independently, the queue must ensure data availability for dependent entities |
| Scalability | Handle load variations — changes in the number of queues, producers, consumers, and messages. Must automatically scale resources up or down |
| Availability | Remain operational for sending and receiving messages even if individual components fail |
| Performance | High throughput and low latency |
Durability is the requirement that separates a queue from a cache
Put this next to distributed caching and the contrast is sharp. A cache may lose everything and still be working correctly — the design of truth is elsewhere.
A queue has no such luxury. Once it acknowledges a message, that message is the only copy. The producer has moved on and will not send it again. Losing it means the email is never sent, the video is never transcoded, the order is never fulfilled — with no error anywhere, because everyone believes the work was accepted.
That single obligation is why this design needs replication, acknowledgment protocols, and explicit deletion, and why none of the cache's "just rebuild it from the database" reasoning applies.
Availability when nodes fail
"How can availability be maintained in a distributed messaging queue when some nodes fail?"
The mechanisms are the ones this design assembles over the next several lessons: replicate queue data across nodes so a failure does not take the only copy, keep frontend servers stateless so any one of them can serve any request, put load balancers in front to route around failed hosts, and give the cluster manager responsibility for detecting failure and reassigning ownership to a healthy node.
The single-server messaging queue
Start with how a queue works on one machine. When producers and consumers reside on the same node, they access the queue using a locking mechanism to prevent data inconsistency. The queue acts as a critical section, allowing only one entity to access data at a time.
Four drawbacks:
| Drawback | Cause |
|---|---|
| High latency | Producers and consumers acquire a lock to access shared state. Under high concurrency this becomes a bottleneck — request latency rises and throughput declines |
| Low availability | No replication, so producer and consumer processes cannot access the queue in the event of failure |
| Lack of durability | No replication, so data may be lost in the event of a system failure |
| Scalability | Handles only a limited number of messages, producers, and consumers |
Single-server queues introduce a single point of failure — if the host or network link fails, the queue becomes unavailable. Lock contention further reduces throughput, and the architecture provides neither horizontal scalability nor strong durability guarantees.
The lock is the interesting failure, because it gets worse with success
Availability and durability fail for the obvious reason — one copy, one machine. The lock is subtler and more instructive.
A queue is a critical section: one entity touches it at a time. That means concurrency does not help — adding producers and consumers does not increase throughput, it increases contention. Past a point, more clients make the system slower in absolute terms, because time goes into acquiring and releasing rather than into work.
So the single-server queue does not degrade gracefully under growth; it degrades inversely with it. That is why Lesson 5's alternative to locking — serialization through a single-threaded processor — matters, and why the fix is not "a bigger machine."
Can we just extend the single-server design?
"Can we extend the design of a single-server messaging queue to a distributed messaging queue?"
To extend the design of a single-server messaging queue to a distributed one, we need to make significant efforts to address the drawbacks outlined above.
The honest reading: it is not an extension, it is a redesign. Every one of the four drawbacks is a consequence of being on one machine, so none of them can be fixed while staying there. What carries over is the abstraction — producers, consumers, FIFO ordering, explicit delete — not the implementation.
Building blocks we will use
A note on message size before we go on. The queue defines a maximum message size, and it is deliberately small — a queue is optimised for many small records moving fast, not for payloads.
The claim-check pattern again. Asked how to send a large payload through a queue, the answer is that you send where it is, not what it is — which also keeps replication cheap, since the queue replicates the reference rather than the bytes.
| Building block | Used for |
|---|---|
| Databases | Store metadata for queues and users |
| Caches | Store frequently accessed data, such as user or queue metadata |
| Load balancers | Distribute incoming requests to metadata servers |
Each of these has its own chapter earlier in this module — the messaging queue composes them rather than reinventing them.
Key takeaway
Five operations, with delete deliberately separate from receive. Four non-functional requirements, of which durability is the one that distinguishes a queue from a cache. And a single-server queue fails all four — the lock being the failure that worsens as you add clients.
Interview signal by level
| Level | What a strong answer sounds like |
|---|---|
| L4 | "Send, receive, and delete messages, and it should be scalable and available." |
| L5 | Names the single-server limits: "one machine is a SPOF with no replication, so no durability, and the lock caps throughput." |
| Staff+ | Explains why the lock is the interesting one: "the queue is a critical section, so adding producers and consumers adds contention rather than throughput — it degrades inversely with load, which is why a bigger machine doesn't help. And durability is non-negotiable here in a way it isn't for a cache: once we ack, ours is the only copy." |
Next: the hardest requirement to satisfy — order.