Fundamentals
What is a Message Queue?
A message queue is an intermediary that allows one component to send work or events without requiring the receiving component to process them immediately.
Producer
Creates message
Queue / Broker
Stores message temporarily
Consumer
Processes message
Decoupling
Producer does not need to know when or how quickly the consumer processes work.
Asynchronous Processing
The request can finish while background work continues.
Traffic Buffering
A queue can absorb temporary spikes while consumers catch up.
Independent Scaling
Producer and consumer can scale separately.
Failure Isolation
Temporary consumer failure does not necessarily lose incoming work.
Load Leveling
Bursty incoming traffic can become smoother downstream processing.
Queue does not automatically mean Kafka
Architecture
Synchronous vs Asynchronous Communication
Queues are especially useful when the caller does not need every downstream activity to finish before receiving a response.
Synchronous
Client
Order API
Notification Service
Caller waits
Useful when the caller needs an immediate answer from the dependency.
Asynchronous
Client
Order API
Message Queue
Notification Worker
Useful when the work can happen independently after the main operation.
Do not make critical user confirmation asynchronous without thinking about semantics
Core Components
Message Queue Architecture
Most messaging systems contain producers, brokers, queues or topics, consumers and some form of acknowledgement or offset tracking.
Producer A
Order service
Producer B
Payment service
Producer C
User service
Message Broker
Queues • Topics • Partitions • Retention
Consumer A
Notification
Consumer B
Analytics
Consumer C
Inventory
A Message Usually Contains
{
"eventId": "evt_99201",
"eventType": "ORDER_CREATED",
"occurredAt": "2026-09-29T14:00:00Z",
"version": 1,
"correlationId": "req_123",
"payload": {
"orderId": "order_501",
"userId": "user_42",
"amount": 1999
}
}eventIdUnique ID useful for deduplication.
eventTypeIdentifies business event.
timestampWhen event occurred.
versionSupports schema evolution.
correlationIdHelps trace one workflow across services.
payloadBusiness data required by consumers.
Decision Guide
Which Messaging Pattern Should You Choose?
Start from business semantics: who needs the message, whether ordering/replay is required, and whether the work is a command, job or event. Technology comes after the pattern.
What does the producer need?
job • event • ordered transition • scheduled work
One Worker
Work / point-to-point queue
Many Systems
Pub/Sub or event stream
Ordering
FIFO / keyed partition
Future Work
Delayed / scheduled queue
Retry
transient failures
DLQ
poison/permanent failures
Idempotency
duplicate-safe business effects
| Requirement | Best starting pattern | Why | Common mistake |
|---|---|---|---|
| One background job -> one worker | Work queue | Simple ownership + horizontal worker scaling | Using pub-sub and accidentally doing the job multiple times |
| Many services react to one fact | Pub/Sub | Each subscriber has independent progress | Putting consumers in one competing queue so only one sees the event |
| Replay historical events | Event stream/log | Retention + offsets | Using a destructive task queue and discovering history is gone |
| Same entity must stay ordered | FIFO group / keyed partition | Order only where business needs it | Demanding global ordering and destroying throughput |
| Run work later | Delayed queue | No repeated DB polling | Assuming expiry is exact and ignoring race with success |
| Temporary failure | Retry queue | Backoff gives dependency time to recover | Immediate infinite retries |
| Permanent/poison failure | DLQ | Isolate + inspect + controlled replay | Treating DLQ as a trash can nobody monitors |
Messaging Pattern 1
Point-to-Point Queue
A producer sends a message to a queue and one consumer instance normally processes that message.
Theory
Point-to-point messaging is the simplest queue model. Producers write independent units of work to a queue. Consumers compete for those messages, and a single message is normally completed by one consumer.
The queue decouples producer speed from consumer speed. If producers temporarily create 20,000 jobs per second while workers process only 10,000, the backlog grows instead of forcing every producer request to wait for workers.
This model is ideal when the work itself should happen once, not once per subscriber. If three independent systems all need the same event, use pub-sub or an event stream instead of putting all three systems behind one competing-consumer queue.
Order API
Creates background job
orders.queue
M1 • M2 • M3
Worker 1
Processes M1
Worker 2
Processes M2
Worker 3
Processes M3
Best when
Avoid / reconsider when
Ordering
Usually no global ordering guarantee unless the queue explicitly provides FIFO semantics.
Delivery
Commonly at-least-once with ACK/delete semantics.
Scaling
Add consumers until worker capacity catches up with production, subject to downstream limits.
Common Technologies
RabbitMQ queues, Amazon SQS standard queues, Azure Service Bus queues, Google Cloud Tasks/Pub/Sub subscriptions depending on semantics.
Good use cases
Main benefit
Simple competing-consumer model with natural horizontal scaling.
Main trade-off
Duplicate delivery is still possible after failures, so consumers should be idempotent.
Interview answer
Messaging Pattern 2
Work Queue / Competing Consumers
Multiple workers consume from the same queue to divide expensive or slow work.
Theory
A work queue is a point-to-point queue specifically used to distribute jobs across a worker pool. Every worker runs the same or compatible processing logic and competes for available work.
The most important design controls are worker concurrency, prefetch/batch size, acknowledgement timing and downstream capacity. Fetching too many jobs per worker can create unfairness; fetching too few can underutilize workers.
Work queues are commonly autoscaled from queue depth, oldest-message age, consumer utilization or processing latency. Queue depth alone is not enough because a queue containing ten 30-minute video jobs is very different from ten 20-millisecond jobs.
Upload API
video.mp4
transcode.jobs
buffered work
Worker A
1080p
Worker B
720p
Worker C
480p
Best when
Avoid / reconsider when
Ordering
Usually weak ordering because multiple workers run concurrently.
Delivery
At-least-once is common; ACK only after successful completion.
Scaling
Autoscale worker count while protecting databases, APIs and other dependencies.
Common Technologies
RabbitMQ, SQS, Celery brokers, cloud task queues, Redis-backed job systems for simpler workloads.
Good use cases
Main benefit
Throughput increases by adding workers while keeping producers simple.
Main trade-off
One slow job can occupy a worker for a long time; use timeouts, visibility extension, job splitting or dedicated queues where needed.
Interview answer
Messaging Pattern 3
Publish / Subscribe
A publisher emits an event without knowing every subscriber; multiple independent subscribers can each receive it.
Theory
Pub-sub is used when an event is interesting to multiple independent systems. A publisher writes to a topic or exchange, and subscriptions independently receive the event.
Each subscriber owns its own processing progress. Notification can fail while analytics succeeds. This independence is one of the biggest benefits of event-driven architecture.
Pub-sub should represent something that happened, such as ORDER_CREATED or PAYMENT_SUCCEEDED, rather than a tightly coupled remote procedure call disguised as a message whenever possible.
Order Service
ORDER_CREATED
orders.created
topic / event bus
Notification
send confirmation
Analytics
record conversion
Warehouse
store event
Best when
Avoid / reconsider when
Ordering
Depends on topic/partition model; often ordered only within a key or partition.
Delivery
Frequently at-least-once per subscription/consumer group.
Scaling
Each subscription scales independently, which prevents one slow consumer from blocking unrelated consumers.
Common Technologies
Kafka topics with different consumer groups, Google Pub/Sub topics/subscriptions, SNS+SQS, RabbitMQ exchanges with multiple queues.
Good use cases
Main benefit
New consumers can be added without changing the producer's core business flow.
Main trade-off
Schema evolution, duplicate events and eventual consistency must be designed explicitly.
Interview answer
Messaging Pattern 4
Fanout / Broadcast
Every bound subscriber receives a copy of every published message.
Theory
Fanout is a broadcast routing pattern. The publisher does not select a destination using a routing key; every bound queue/subscriber gets the message.
It is useful for system-wide events where all subscribers care about the same information. It is intentionally less selective than topic routing.
Because one input message becomes many deliveries, fanout can multiply traffic substantially. Payload size, subscriber count and retry behavior should be considered before broadcasting high-volume events.
Config Service
CONFIG_CHANGED
fanout exchange
broadcast
Service A
refresh config
Service B
refresh config
Service C
refresh config
Best when
Avoid / reconsider when
Ordering
Ordering is normally determined independently for each subscriber/queue.
Delivery
Each subscriber gets its own delivery/acknowledgement lifecycle.
Scaling
Subscribers scale independently, but publisher fanout amplification must be budgeted.
Common Technologies
RabbitMQ fanout exchange, SNS fanout to SQS, pub-sub systems with multiple subscriptions.
Good use cases
Main benefit
Very simple broadcast semantics.
Main trade-off
Network/storage cost grows with the number of subscribers.
Interview answer
Messaging Pattern 5
Topic-Based Routing
Subscribers receive only events whose topic/routing key matches their subscription pattern.
Theory
Topic routing sits between one-to-one queues and full broadcast. Producers publish a meaningful routing key such as payment.success, payment.failed or order.created.
Subscribers bind to exact keys or patterns such as payment.*. This keeps unrelated traffic away from consumers and makes ownership clearer.
Topic taxonomies are part of architecture. Without naming conventions and schema ownership, organizations accumulate ambiguous event names and brittle consumers.
Payment Service
routingKey=payment.success
payments exchange
topic routing
Booking Queue
payment.success
Fraud Queue
payment.*
Failure Queue
payment.failed
Best when
Avoid / reconsider when
Ordering
Usually ordering is scoped to a topic/partition/queue, not globally across unrelated keys.
Delivery
Broker dependent; often durable at-least-once.
Scaling
Different routing keys can feed independent queues and worker pools.
Common Technologies
RabbitMQ topic exchanges, Kafka topic naming conventions, cloud pub-sub filters, SNS filtering.
Good use cases
Main benefit
Reduces unnecessary deliveries while keeping publishers decoupled.
Main trade-off
Topic and schema governance become important at scale.
Interview answer
Messaging Pattern 6
FIFO / Ordered Queue
Messages are processed in an intended sequence, often globally or within a message group/key.
Theory
FIFO is needed when processing M2 before M1 would produce an invalid state. A common example is CREATED -> PAID -> SHIPPED for the same order.
The strongest form, global ordering, severely limits parallelism because all messages effectively pass through one ordering lane. Large systems therefore prefer per-entity or per-key ordering.
A practical design hashes orderId, bookingId or accountId to a partition/message group. Different entities run in parallel while events for the same entity remain ordered.
Order Service
key=order-501
Partition / FIFO Group
CREATED -> PAID -> SHIPPED
Order Consumer
applies sequence
Best when
Avoid / reconsider when
Ordering
Global FIFO or, preferably, per-key/message-group FIFO.
Delivery
Can still be at-least-once; FIFO does not automatically mean exactly-once business effects.
Scaling
Scale across many keys/partitions rather than trying to parallelize one strict ordered stream.
Common Technologies
Kafka partition ordering, SQS FIFO message groups, RabbitMQ single-active-consumer or carefully designed queues.
Good use cases
Main benefit
Prevents out-of-order state transitions where sequence matters.
Main trade-off
Ordering reduces parallelism and one slow/poison message can block later messages in the same ordered lane.
Interview answer
Messaging Pattern 7
Priority Queue
Higher-priority work is processed ahead of lower-priority work.
Theory
Priority queues are useful when not all work has equal urgency. Security incidents, payment reconciliation or premium interactive jobs may need to jump ahead of marketing or batch work.
A single priority number is not enough for production design. You must prevent starvation of low-priority work through aging, weighted scheduling or reserved worker capacity.
Many teams use separate queues per priority because it gives clearer capacity controls and avoids broker-specific priority behavior.
Job Producers
P1 / P2 / P3
Priority Scheduler
P1 first, fairness applied
Critical Worker
P1 security
Normal Worker
P2 payments
Bulk Worker
P3 marketing
Best when
Avoid / reconsider when
Ordering
Priority usually overrides pure FIFO across priority levels; FIFO may still hold within one level.
Delivery
Same as the underlying queue; priority changes scheduling, not reliability semantics.
Scaling
Often use separate worker pools or reserved capacity for high-priority lanes.
Common Technologies
RabbitMQ priority queues, separate SQS queues, Redis/job frameworks with priority lanes.
Good use cases
Main benefit
Critical work is not trapped behind a large low-priority backlog.
Main trade-off
Low-priority jobs can starve if scheduling has no fairness mechanism.
Interview answer
Messaging Pattern 8
Delayed / Scheduled Queue
A message becomes eligible for processing only after a delay or scheduled timestamp.
Theory
Delayed queues represent future work without repeatedly scanning a database. The broker or scheduler stores a job until its due time and then makes it consumable.
This is useful for booking expiry, retry-after delays, reminder notifications and cart abandonment. The delay should be treated as approximate unless the technology explicitly provides strict scheduling guarantees.
For extremely large scheduling horizons or millions of long-term timers, consider whether a dedicated scheduler plus durable database is more appropriate than keeping every future task inside a broker.
Booking Service
expire hold at T+5m
Delayed Queue
hidden until due time
Expiry Worker
HELD -> AVAILABLE if still unpaid
Best when
Avoid / reconsider when
Ordering
Due-time order may not be exact under retries and distributed processing.
Delivery
Normally inherits at-least-once semantics from the underlying queue.
Scaling
Partition timers/scheduled jobs and make handlers idempotent because expiry can race with success events.
Common Technologies
SQS delay queues/timers, RabbitMQ delayed-message patterns, cloud schedulers + queues, Redis sorted sets for controlled workloads.
Good use cases
Main benefit
Avoids wasteful database polling for every future task.
Main trade-off
Clock behavior, cancellation and large scheduled-job volume must be handled carefully.
Interview answer
Messaging Pattern 9
Retry Queue
Temporarily failed work is retried after controlled backoff instead of immediately hammering the dependency.
Theory
Retries are for transient failure: network timeouts, temporary 5xx responses, rate limits or short dependency outages. Permanent validation errors should not loop through retries forever.
A strong retry design uses bounded attempts, exponential backoff and jitter. Jitter prevents thousands of failed messages from retrying at exactly the same second and recreating the outage.
Many architectures use retry tiers such as 5 seconds, 30 seconds and 5 minutes before moving to a DLQ. The original attempt count and failure reason should travel with the message metadata.
Consumer
provider timeout
Retry Tier
5s -> 30s -> 5m + jitter
Consumer Retry
success -> ACK
DLQ
max attempts
Best when
Avoid / reconsider when
Ordering
Retries can break original ordering unless retry behavior is integrated with the ordered partition/group.
Delivery
At-least-once by design; duplicate-safe handlers are mandatory.
Scaling
Separate retry traffic from fresh traffic when old failures could overwhelm the healthy path.
Common Technologies
Retry queues/exchanges, SQS redrive policies, Kafka retry topics, application-level scheduled retries.
Good use cases
Main benefit
Allows dependencies to recover without losing work.
Main trade-off
Unlimited/immediate retries create retry storms and can make an outage worse.
Interview answer
Messaging Pattern 10
Dead Letter Queue (DLQ)
Messages that cannot be processed successfully are isolated for investigation and controlled replay.
Theory
A DLQ prevents poison messages from blocking healthy traffic forever. Messages usually arrive after exhausting retries, failing validation or violating processing rules.
The DLQ is not the end of the design. Production systems need alerts, dashboards, failure metadata, inspection tooling and a safe replay path after the underlying bug or data issue is fixed.
Blindly replaying an entire DLQ can repeat the outage. Replay should be rate-limited, observable and often filtered by failure reason or event version.
Main Consumer
fails repeatedly
DLQ
payload + reason + attempts
Alerting
page/notify owner
Replay Tool
fix -> controlled replay
Best when
Avoid / reconsider when
Ordering
Moving one failed message out can allow later messages to progress, which may affect ordering semantics.
Delivery
Stores failed deliveries for later inspection/replay.
Scaling
DLQ volume should normally be low; sudden growth is an incident signal, not a scaling target.
Common Technologies
SQS DLQ, RabbitMQ dead-letter exchange, Kafka dead-letter topics, Service Bus dead-letter subqueues.
Good use cases
Main benefit
Bad messages stop poisoning the normal processing path.
Main trade-off
An unmonitored DLQ becomes a silent data-loss graveyard.
Interview answer
Messaging Pattern 11
Request-Reply over Messaging
A requester sends a command with correlation/reply metadata and waits for a response message.
Theory
Request-reply can be built on a broker when asynchronous transport is required but the caller still expects a response. The request carries a correlationId and usually a reply destination.
This pattern is more complex than normal HTTP/gRPC because you must handle timeouts, abandoned replies, correlation state and duplicate responses. It should not be chosen merely to avoid synchronous APIs.
It is useful across unreliable or disconnected boundaries, long-running operations or messaging-only environments. For ordinary low-latency service-to-service calls, HTTP/gRPC is often simpler.
Report API
correlationId=req-42
report.request
replyTo=report.reply
Report Worker
publishes correlated result
Reply Queue
req-42 response
Best when
Avoid / reconsider when
Ordering
Usually based on correlation/request IDs rather than global ordering.
Delivery
Both request and reply paths need reliability/idempotency decisions.
Scaling
Workers scale like a work queue; response routing must remain correlated to callers.
Common Technologies
RabbitMQ reply-to patterns, JMS request/reply, custom broker-based command-response workflows.
Good use cases
Main benefit
Keeps queued transport while still supporting correlated responses.
Main trade-off
Timeouts, duplicate responses and reply-queue lifecycle are harder than ordinary one-way messaging.
Interview answer
Messaging Pattern 12
Event Stream / Append-Only Log
Events are appended to a durable ordered log and retained so consumers can replay them independently.
Theory
A stream/log differs from a traditional destructive queue. Consuming an event usually advances a consumer offset rather than deleting the event immediately for everyone.
Retention makes replay possible. A new analytics consumer can start from older offsets, or an existing consumer can rebuild state after a bug fix.
Partitioning provides scale. Events with the same key are routed to the same partition for ordered processing, while unrelated keys are processed in parallel across partitions.
Event Producers
append events
orders topic
P0 | P1 | P2 + retained offsets
Analytics Group
own offsets
Notification Group
own offsets
Warehouse Group
replay/history
Best when
Avoid / reconsider when
Ordering
Ordered within a partition, not globally across all partitions.
Delivery
Commonly at-least-once; stronger semantics may exist within a defined platform scope.
Scaling
Increase partitions for parallelism, then scale consumers within each consumer group up to partition count.
Common Technologies
Apache Kafka, Redpanda, Amazon Kinesis, Pulsar-like streaming platforms.
Good use cases
Main benefit
Durable history can be replayed by many independent consumers.
Main trade-off
Partitioning, retention, offset management and schema evolution add operational/design complexity.
Interview answer
Kafka Concept
Consumer Groups
Consumer groups allow multiple consumers to cooperatively process partitions while independent groups can read the same stream separately.
Partition 0
Partition 1
Partition 2
Consumer 1
P0
Consumer 2
P1
Consumer 3
P2
Important scaling rule
Different Consumer Groups
orders topic
notification-group
Reads all orders
analytics-group
Reads all orders
warehouse-group
Reads all orders
Reliability
Message Delivery Guarantees
Delivery semantics define what can happen when producers, brokers or consumers fail.
At-Most-Once
0 or 1 delivery
Message is not retried in a way that creates duplicates. Failure may cause data loss.
No duplicate processing
Messages may be lost
At-Least-Once
1 or more deliveries
Failed or unacknowledged messages are retried. Duplicates are possible.
Lower risk of message loss
Consumer must handle duplicate delivery
Exactly-Once
One business effect
Exactly-once processing is difficult across distributed side effects and usually depends on carefully defined scope, transactions or idempotency.
Exactly once needs a precise definition
Correctness
Message Ordering
Ordering requirements directly affect partitioning and parallelism.
Global Ordering
M1
M2
M3
M4
M5Every message needs one global order. This often limits parallelism.
Per-Key Ordering
order-1:
CREATED -> PAID -> SHIPPED
order-2:
CREATED -> CANCELLEDOften enough in real systems. Events for the same entity are routed to the same partition.
partition =
hash(orderId)
% partitionCount;
// Same orderId
// -> same partition
// -> ordered processing
Prefer the narrowest ordering guarantee that solves the business problem
Message Lifecycle
Acknowledgements & Visibility Timeout
The broker needs a way to determine whether processing completed successfully.
Queue
Message available
Consumer
Processing...
Message Complete
Remove / commit progress
What if Consumer Crashes Before ACK?
Consumer A
Gets M1
No ACK
Message is not considered complete
Consumer B
Processes M1 again
Visibility timeout
Critical Concept
Idempotent Consumers
If the same message is delivered twice, processing it twice should not accidentally duplicate the business operation.
Bad Consumer
async function handlePayment(message) {
// If message is delivered twice,
// customer may be charged twice.
await paymentProvider.charge(
message.userId,
message.amount
);
}Better Pattern
async function handleEvent(event) {
const existing =
await processedEvents.find(
event.eventId
);
if (existing) {
return;
}
await database.transaction(
async (tx) => {
await processBusinessLogic(
tx,
event
);
await tx.processedEvents.insert({
eventId: event.eventId
});
}
);
}Unique Event ID
Every event should have a stable identifier.
Idempotency Key
Use a business key or client-provided key for operations that may be retried.
Unique DB Constraint
Database constraints can prevent repeated insertion of the same effect.
Processed Event Table
Track which events were already successfully applied.
Resilience
Retry Strategy
Retries should give temporary failures time to recover instead of immediately repeating the same failing operation.
Attempt 1 -> immediately
Attempt 2 -> 2 seconds
Attempt 3 -> 4 seconds
Attempt 4 -> 8 seconds
Attempt 5 -> 16 seconds
Then -> DLQdelay =
min(
MAX_DELAY,
BASE_DELAY * (2 ** retryCount)
);
jitter =
random(0, 1000);
nextRetry =
delay + jitter;
Why jitter?
Retry or Don't Retry?
Retry
• Temporary network failure
• Dependency timeout
• Temporary 5xx
• Rate limit with known retry behavior
Usually Don't Blindly Retry
• Invalid schema
• Missing required field
• Business rule violation
• Permanently invalid account
Traffic Spikes
Backpressure
A queue helps when producers are temporarily faster than consumers, but an infinitely growing queue is not a complete scaling strategy.
Incoming Traffic
100K jobs/sec
Queue
Growing backlog
Consumers
Cannot keep up
Ways to Handle It
Autoscale Consumers
Increase workers based on lag or queue depth.
Rate Limit Producers
Reject, delay or throttle non-critical production.
Batch Processing
Consume multiple messages at once when downstream operations benefit.
Prioritize
Process critical traffic before less important jobs.
Load Shed
Drop optional work when the system is under severe pressure.
Bound Queue Growth
Apply retention, quotas and capacity limits rather than allowing infinite backlog.
Horizontal Scale
Partitions
A single ordered queue can limit throughput. Distributed logs often split a topic into partitions.
Producer
key = orderId
Partition 0
order A, D, G
Partition 1
order B, E, H
Partition 2
order C, F, I
More Partitions
Can increase parallel processing and throughput.
Same Key
Route related events to the same partition when per-key ordering matters.
Hot partition problem
Durability
Broker Replication & Persistence
If a broker crashes, important messages should not disappear simply because they existed on only one machine.
Partition Leader
Receives writes
Replica 1
Replica 2
Messaging systems differ in how they persist messages, acknowledge producer writes, replicate partitions and elect replacement leaders. Higher durability generally introduces additional latency or resource cost.
Kafka Deep Dive
Kafka Internals: Topics, Partitions, Leaders, Offsets & Consumer Groups
Kafka is best understood as a partitioned retained log, not merely as a faster queue.
Producer A
key=order-1
Producer B
key=order-2
Producer C
key=order-3
Partition 0
leader + replicas • offsets 0..N
Partition 1
leader + replicas • offsets 0..N
Partition 2
leader + replicas • offsets 0..N
Consumer 1
P0
Consumer 2
P1
Consumer 3
P2
Topic
Logical event category such as booking.events. A topic is split into partitions for throughput and parallelism.
Partition
An ordered append-only log. Ordering is guaranteed inside one partition, not across the whole topic.
Offset
Position of a record in a partition. Consumer groups track progress using offsets.
Leader & Replicas
One broker leader handles partition writes/reads while replicas provide durability and failover depending on configuration.
Consumer Group
Consumers cooperate so each partition is owned by at most one active consumer in that group at a time.
Retention
Events remain for a configured period/size even after consumption, enabling replay and new consumers.
Rebalance
Partition ownership moves when consumers join/leave or topology changes. Rebalances can temporarily pause processing and must be considered in long-running handlers.
Hot Partition
A skewed key sends disproportionate traffic to one partition, limiting throughput even when other partitions are idle.
More partitions are not free
RabbitMQ Deep Dive
RabbitMQ Internals: Exchanges, Bindings, Queues, ACKs & Prefetch
RabbitMQ is a traditional message broker with powerful routing before messages reach queues.
Producer
routingKey=payment.success
Exchange
direct • topic • fanout • headers
booking.queue
payment.success
fraud.queue
payment.*
audit.queue
all payment events
Workers
prefetch controls in-flight messages
| Exchange | Routing rule | Good for | Example |
|---|---|---|---|
| Direct | Exact routing-key match | Simple explicit routing | payment.success -> booking.queue |
| Topic | Wildcard patterns | Event families | payment.* or order.# |
| Fanout | Broadcast to every binding | All subscribers need every message | config.changed -> all services |
| Headers | Header-based matching | Special metadata routing | x-region=IN + x-tier=premium |
Prefetch is a throughput/fairness control
Cloud Queues
Managed Queues: Standard vs FIFO, Visibility Timeout, Long Polling & DLQ
Managed services remove broker-cluster operations, but you still need to understand their delivery semantics and limits.
Standard Queue
Use for high-throughput independent jobs where strict order is unnecessary. Expect at-least-once-style duplicate possibility and make handlers idempotent.
✓ High parallelism
✓ Simple worker scaling
✓ Great default for background jobs
✕ Do not depend on strict global ordering
FIFO / Message Groups
Use when events for the same key must be ordered. Different message groups can still progress in parallel.
✓ Per-group ordering
✓ Useful for order/account transitions
✓ Parallelism across groups
✕ More constraints than standard queues
Visibility Timeout
Hide an in-flight message; redeliver if the consumer does not finish in time.
Long Polling
Wait for messages instead of repeatedly issuing empty receives, reducing waste and latency.
Redrive / DLQ
Move repeatedly failing messages to a DLQ after a configured receive count.
Delay
Keep a message unavailable until a future delay expires for retries or scheduled work.
Technology Selection
Kafka vs RabbitMQ vs Managed Queues
Do not choose based only on popularity. Their messaging models are different.
| Feature | Kafka-style Log | RabbitMQ-style Broker | Managed Queue |
|---|---|---|---|
| Core model | Distributed event log | Message broker / queues | Managed queue service |
| Replay | Strong fit | Usually queue-consumption oriented | Service dependent |
| Routing | Topics + partitions | Rich exchanges / routing | Queues + topics depending on service |
| High-throughput streams | Excellent fit | Possible, but not its only strength | Depends on managed service |
| Task queue | Possible | Very natural | Very natural |
| Operational effort | Can be significant | Can be significant | Provider handles more infrastructure |
| Typical use | Events, CDC, streams, analytics pipelines | Jobs, routing, business messaging | Cloud-native asynchronous jobs |
Choose Kafka-like Stream
When event retention, replay, high-throughput streams, consumer groups and partitioned logs are central.
Choose RabbitMQ-like Broker
When task queues, routing, acknowledgements and traditional business messaging are the main need.
Choose Managed Queue
When you want queue semantics without operating broker clusters yourself.
Detailed Technology Decision
Kafka / Distributed Log
Choose when
You need retained events, replay, very high throughput, partitions, consumer groups, CDC or stream processing.
Reconsider when
You only need a tiny task queue with simple per-message routing and do not need replay/retention complexity.
Strengths
Trade-offs
RabbitMQ / Traditional Broker
Choose when
You need work queues, acknowledgements, flexible routing, exchanges, per-message delivery controls and business messaging.
Reconsider when
Your primary requirement is a long-retained high-throughput event history that many consumers replay independently.
Strengths
Trade-offs
Managed Queue (SQS-style)
Choose when
You want durable asynchronous task processing without operating broker clusters yourself.
Reconsider when
You need sophisticated broker-side routing or long replayable streams as the central abstraction.
Strengths
Trade-offs
Managed Pub/Sub
Choose when
Many independent cloud services need the same event and you want managed topic/subscription scaling.
Reconsider when
Only one worker should own a task and no broadcast/subscription model is needed.
Strengths
Trade-offs
Fast interview selection examples
10M analytics events/day + replay
Start with: Kafka-style log
Generate invoice once in background
Start with: Work queue / managed queue
Order event must reach analytics + notification + warehouse
Start with: Pub/Sub or event stream with separate consumer groups
payment.success must route to booking, payment.failed to recovery
Start with: Topic/direct routing broker
Release booking hold after 5 minutes
Start with: Delayed queue / scheduler + queue
Same order events must stay sequential
Start with: FIFO message group or partition keyed by orderId
Small team wants durable jobs with minimal operations
Start with: Managed queue
Need replayable CDC pipeline into warehouse
Start with: Kafka/Kinesis/Pulsar-style stream
Distributed Transactions
The Dual-Write Problem & Transactional Outbox
A common failure occurs when an application must update its database and publish an event as two separate operations.
The Problem
Order Service
Database
COMMIT succeeds
Broker Publish Fails
Order exists but event is missing
Transactional Outbox
BEGIN TRANSACTION;
INSERT INTO orders (...);
INSERT INTO outbox (
event_id,
event_type,
payload
)
VALUES (
'evt_123',
'ORDER_CREATED',
'{...}'
);
COMMIT;Application DB
Order + Outbox committed atomically
Message Broker
ORDER_CREATED
Why it helps
Implementation
Code Examples: Producer, Consumer, Idempotency & Retry
In interviews you usually explain the design rather than write framework code, but these examples make the message lifecycle concrete.
1. Event Envelope
{
"eventId": "evt_01H...",
"eventType": "BOOKING_CONFIRMED",
"version": 1,
"occurredAt": "2026-09-30T10:30:00Z",
"correlationId": "req_123",
"partitionKey": "booking_991",
"payload": {
"bookingId": "booking_991",
"userId": "user_42",
"showId": "show_77",
"seatIds": ["A10", "A11"]
}
}2. Spring Kafka Producer
@Service
@RequiredArgsConstructor
public class BookingEventPublisher {
private final KafkaTemplate<String, BookingEvent> kafkaTemplate;
public CompletableFuture<SendResult<String, BookingEvent>> publish(
BookingEvent event) {
// bookingId is used as the key so events for the same
// booking are routed to the same partition.
return kafkaTemplate.send(
"booking.events",
event.getBookingId(),
event
);
}
}3. Idempotent Spring Kafka Consumer
@Service
@RequiredArgsConstructor
public class BookingNotificationConsumer {
private final ProcessedEventRepository processedEventRepository;
private final NotificationService notificationService;
@KafkaListener(
topics = "booking.events",
groupId = "notification-group"
)
@Transactional
public void consume(BookingEvent event) {
if (processedEventRepository.existsById(event.getEventId())) {
return; // duplicate delivery -> safe no-op
}
notificationService.createNotificationRecord(event);
processedEventRepository.save(
new ProcessedEvent(event.getEventId())
);
}
}External side effects need their own idempotency
4. Retry Classification
public RetryDecision classify(Throwable error, int attempt) {
if (error instanceof ValidationException) {
return RetryDecision.DLQ; // permanent failure
}
if (attempt >= 5) {
return RetryDecision.DLQ;
}
if (error instanceof TimeoutException ||
error instanceof TemporaryProviderException) {
return RetryDecision.RETRY_WITH_BACKOFF;
}
return RetryDecision.DLQ;
}5. Transactional Outbox Worker
// Transaction 1: business write + outbox write
BEGIN;
UPDATE bookings
SET status = 'CONFIRMED'
WHERE booking_id = 'booking_991';
INSERT INTO outbox(event_id, aggregate_id, event_type, payload)
VALUES ('evt_123', 'booking_991', 'BOOKING_CONFIRMED', '{...}');
COMMIT;
// Async publisher / CDC later publishes evt_123.
// After broker acknowledgement, mark the outbox row published.
// If publishing retries, eventId keeps downstream processing idempotent.End-to-End Case Study
BookMyShow-Style Booking: Where Queues Help and Where They Do Not
A booking system mixes synchronous concurrency control with asynchronous event processing. The hardest interview skill is choosing the correct boundary.
A queue is not the seat lock
1. Read / Discovery Path
Browsing movies, theatres and show availability is read-heavy. Keep this path fast using CDN/cache/read replicas/search indexes. Do not insert a queue into a normal read request just because the system uses queues elsewhere.
User
searches movie/show
API / Load Balancer
routes synchronous read
Search Service
movies/theatres
Cache / Read Store
show metadata
Availability Read Model
seat status snapshot
2. Critical Seat-Hold Path — Keep It Synchronous
User A
selects A10 + A11
Seat Lock Service
atomic conditional acquire
Seat / Booking Store
AVAILABLE -> HELD, expiresAt=T+5m
Hold Created
bookingId + expiry
UPDATE seats
SET status = 'HELD',
held_by = :bookingId,
expires_at = :expiresAt,
version = version + 1
WHERE show_id = :showId
AND seat_id = :seatId
AND status = 'AVAILABLE';
// affectedRows == 1 -> lock acquired
// affectedRows == 0 -> another user already owns it3. Create an Expiry Event
After the hold succeeds, publish or schedule a delayed expiry job. The job contains bookingId, seat IDs and preferably a hold/version token. The expiry worker must re-read authoritative state before releasing anything.
Seat Hold Created
A10 held until 10:35
booking.expiry
delayed queue
Expiry Worker
release only if still HELD and version matches
4. Payment Success and the Race with Expiry
Payment can complete near the exact time the expiry event fires. Do not depend on delivery order between unrelated systems. Both handlers must use state/version checks so one becomes a safe no-op when the other has already won.
Payment Callback
PAYMENT_SUCCEEDED
Expiry Event
HOLD_EXPIRED
Booking State Machine
authoritative status + version decides
CONFIRMED
expiry becomes no-op
EXPIRED
late/invalid payment goes to reconciliation
5. Transactional Booking Confirmation + Outbox
Once payment is valid, confirm the booking and create BOOKING_CONFIRMED in an outbox inside the same database transaction. This avoids the dual-write failure where the booking is confirmed but downstream ticket/notification events are never published.
BEGIN;
UPDATE bookings
SET status = 'CONFIRMED',
payment_id = :paymentId
WHERE booking_id = :bookingId
AND status = 'PENDING';
UPDATE seats
SET status = 'BOOKED'
WHERE held_by = :bookingId
AND status = 'HELD';
INSERT INTO outbox(
event_id,
aggregate_id,
event_type,
payload
)
VALUES(
:eventId,
:bookingId,
'BOOKING_CONFIRMED',
:payload
);
COMMIT;6. Async Fanout After Booking Confirmation
Outbox / CDC
BOOKING_CONFIRMED
booking.events
retained topic / pub-sub
Notification
email / SMS / push
Ticket Worker
generate QR / PDF
Analytics
conversion metrics
Warehouse
event pipeline
Why this boundary works
7. Notification Retry and DLQ
Notification Consumer
email provider timeout
Retry Topic / Queue
5s -> 30s -> 5m + jitter
notification.dlq
alert + inspect + replay
8. Which Parts Should Be Sync vs Async?
| Operation | Sync / Async | Reason | Queue pattern |
|---|---|---|---|
| Search shows | Sync | User needs immediate result | None |
| Acquire seat lock | Sync | Immediate correctness decision | None for the lock itself |
| Create payment intent | Usually Sync | User needs payment flow now | Events may follow |
| Payment callback processing | Durable + idempotent | Must survive retries/duplicates | Event/queue can decouple downstream work |
| Seat-hold expiry | Async delayed | Future action at T+N | Delayed queue |
| Ticket generation | Async | Can finish after booking confirmation | Work queue |
| Email/SMS | Async | Provider slowness must not fail booking | Work queue + retry + DLQ |
| Analytics | Async | Eventually consistent side effect | Pub/Sub / event stream |
9. BookMyShow Failure Scenarios You Should Explain
Payment succeeds, callback duplicated
Use payment/provider idempotency key + unique payment transaction + idempotent booking transition.
Payment succeeds, callback delayed past hold
Move to reconciliation. Never blindly book a seat that another user may now own.
Expiry event delivered twice
Release using conditional state/version check; second event becomes a no-op.
Email provider down
Booking remains confirmed; retry notification asynchronously and DLQ if exhausted.
Broker temporarily down after booking commit
Transactional outbox preserves the event until publisher/CDC can deliver it.
Consumer commits DB but crashes before ACK
Message is redelivered; eventId/business keys keep processing idempotent.
Traffic spike for blockbuster release
Queue non-critical work, autoscale consumers, protect DB/provider with rate limits and watch oldest-message age.
One show creates a hot Kafka partition
Choose partition keys carefully; avoid a key that funnels the entire hot show into one partition unless ordering requires it.
Production Engineering
Message Queue Failure Scenarios
Messaging systems move complexity rather than eliminating it. You still need to design for consumer crashes, duplicates, backlog and poison messages.
Consumer Crashes
Do not acknowledge unfinished work. The message should become available to another consumer according to the broker model.
Duplicate Message
Use idempotency keys, unique constraints or processed-event tracking so the same event does not apply the business effect twice.
Poison Message
A permanently invalid event should eventually move to a dead letter queue instead of retrying forever.
Consumer Is Too Slow
Monitor queue depth or consumer lag, scale workers and investigate downstream bottlenecks.
Broker Node Fails
Use broker replication, durable storage and appropriate producer acknowledgement configuration.
Producer Publishes but Crashes
Producer retries may create duplicates. Stable event IDs and idempotency are important.
DB Commit Succeeds but Event Publish Fails
Use transactional outbox or an equivalent reliable event publication pattern.
Retry Storm
Use exponential backoff, jitter and bounded retries. Immediate retries can overload an already failing dependency.
Queue Backlog Grows Forever
Autoscale consumers, throttle production, prioritize traffic and alert on sustained lag.
Schema Changes
Version event contracts and design consumers to handle compatible schema evolution.
Observability
What Should You Monitor?
Queue health is often visible through lag, age, failures and throughput before users notice a complete outage.
Queue depth alone is not enough
Interview Preparation
Message Queue Interview Questions with Detailed Answers
These questions cover fundamentals, pattern selection, failure handling, Kafka, RabbitMQ, managed queues, scaling and BookMyShow-style system design.
1Fundamentals
Why do we use a message queue in system design?
+
Fundamentals
Why do we use a message queue in system design?
2Fundamentals
When should I NOT use a message queue?
+
Fundamentals
When should I NOT use a message queue?
3Patterns
Queue vs pub-sub — what is the difference?
+
Patterns
Queue vs pub-sub — what is the difference?
4Patterns
Fanout vs topic routing?
+
Patterns
Fanout vs topic routing?
5Patterns
FIFO vs standard queue — which is better?
+
Patterns
FIFO vs standard queue — which is better?
6Reliability
At-most-once vs at-least-once vs exactly-once?
+
Reliability
At-most-once vs at-least-once vs exactly-once?
7Reliability
Why can duplicate delivery happen even after a consumer successfully processes a message?
+
Reliability
Why can duplicate delivery happen even after a consumer successfully processes a message?
8Reliability
How do you make a consumer idempotent?
+
Reliability
How do you make a consumer idempotent?
9Reliability
What is a poison message?
+
Reliability
What is a poison message?
10Reliability
How should retries be designed?
+
Reliability
How should retries be designed?
11Reliability
What is a visibility timeout?
+
Reliability
What is a visibility timeout?
12Ordering
How do you preserve order without destroying throughput?
+
Ordering
How do you preserve order without destroying throughput?
13Ordering
What happens when one message in an ordered stream keeps failing?
+
Ordering
What happens when one message in an ordered stream keeps failing?
14Scaling
What is backpressure?
+
Scaling
What is backpressure?
15Scaling
Which metric is more useful: queue depth or oldest-message age?
+
Scaling
Which metric is more useful: queue depth or oldest-message age?
16Kafka
Why does Kafka use partitions?
+
Kafka
Why does Kafka use partitions?
17Kafka
What happens if a consumer group has 10 consumers but the topic has 3 partitions?
+
Kafka
What happens if a consumer group has 10 consumers but the topic has 3 partitions?
18Kafka
What is consumer lag?
+
Kafka
What is consumer lag?
19Kafka
How do you choose a Kafka partition key?
+
Kafka
How do you choose a Kafka partition key?
20RabbitMQ
Direct vs topic vs fanout exchange?
+
RabbitMQ
Direct vs topic vs fanout exchange?
21RabbitMQ
What is prefetch and why does it matter?
+
RabbitMQ
What is prefetch and why does it matter?
22Managed Queue
When would you choose SQS-style managed queues instead of Kafka?
+
Managed Queue
When would you choose SQS-style managed queues instead of Kafka?
23Architecture
What is the dual-write problem?
+
Architecture
What is the dual-write problem?
24Architecture
What is the transactional inbox pattern?
+
Architecture
What is the transactional inbox pattern?
25Architecture
Command vs event — what should go on the queue?
+
Architecture
Command vs event — what should go on the queue?
26BookMyShow
Should BookMyShow put seat locking behind a queue?
+
BookMyShow
Should BookMyShow put seat locking behind a queue?
27BookMyShow
Does a queue prevent double booking?
+
BookMyShow
Does a queue prevent double booking?
28BookMyShow
How would you release a seat after the payment window expires?
+
BookMyShow
How would you release a seat after the payment window expires?
29Scenario
How would you send 10 million emails without overloading the provider?
+
Scenario
How would you send 10 million emails without overloading the provider?
30Scenario
A queue backlog is growing even after doubling workers. What do you check?
+
Scenario
A queue backlog is growing even after doubling workers. What do you check?
31Scenario
How do you safely replay a DLQ?
+
Scenario
How do you safely replay a DLQ?
32Scenario
How do you evolve message schemas without breaking consumers?
+
Scenario
How do you evolve message schemas without breaking consumers?
Quick Revision
Frequently Asked Questions
Short answers for the most important message-queue concepts.
Why do we use a message queue in system design?
Message queues decouple producers from consumers, enable asynchronous processing, absorb traffic bursts and allow workloads to be processed independently.
What is the difference between a queue and pub-sub?
In a traditional work queue one message is generally handled by one consumer, while pub-sub allows multiple independent subscribers to receive the same published event.
What is a dead letter queue?
A dead letter queue stores messages that could not be processed successfully after configured retry attempts or because they violate processing rules.
What is at-least-once delivery?
At-least-once delivery means the system retries until the message is processed or acknowledged, so the same message may be delivered more than once.
Why must consumers be idempotent?
Distributed messaging systems can deliver duplicates due to retries or failures, so idempotent consumers prevent repeated delivery from applying the same business effect multiple times.
When should I choose Kafka instead of RabbitMQ?
Choose Kafka-style logs when retained event history, replay, high-throughput streams, partitions and independent consumer groups are central. Choose RabbitMQ-style brokers when flexible routing, traditional work queues and acknowledgement-driven business messaging are the primary need.
Does a message queue guarantee exactly-once business processing?
Not automatically. Distributed failures can still create duplicate deliveries or uncertain side effects. Exactly-once business effects normally require a precise scope plus idempotency, transactions, unique constraints or provider idempotency keys.
Where should queues be used in a BookMyShow-style booking system?
Keep seat locking and immediate booking decisions synchronous and atomic. Use queues for delayed seat-hold expiry, payment events, ticket generation, notifications, analytics, retries and other asynchronous side effects.
Learn how queues fit into complete system designs.
Go beyond individual concepts. Learn how caching, databases, load balancing, queues, rate limiting, concurrency, locking and failure handling work together in interview systems such as BookMyShow, Uber and other scalable platforms.
Continue Learning