HLDMessage Queues70+ min detailed guide

Message Queues & Event-Driven Systems

Learn how queues decouple services, absorb traffic spikes and power asynchronous systems. Understand work queues, pub-sub, Kafka, retries, DLQs, delivery guarantees, ordering, consumer groups, idempotency and failure handling.

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

Publish

Queue / Broker

Stores message temporarily

Consume

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

First identify what communication model you need: task queue, pub-sub, replayable stream, ordered events, delayed execution or routing. Then choose the technology.

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

Wait

Notification Service

Caller waits

Useful when the caller needs an immediate answer from the dependency.

Asynchronous

Client

Order API

Publish and return

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

If a user needs to know whether payment succeeded, simply placing everything behind a queue may not provide the required interaction. Async boundaries should follow business 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

Messages / Events

Message Broker

Queues • Topics • Partitions • Retention

Consumer A

Notification

Consumer B

Analytics

Consumer C

Inventory

A Message Usually Contains

Event Example
{
  "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
  }
}
eventId

Unique ID useful for deduplication.

eventType

Identifies business event.

timestamp

When event occurred.

version

Supports schema evolution.

correlationId

Helps trace one workflow across services.

payload

Business 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?

One Worker

Work / point-to-point queue

Many Systems

Pub/Sub or event stream

Ordering

FIFO / keyed partition

Future Work

Delayed / scheduled queue

then add reliability behavior

Retry

transient failures

DLQ

poison/permanent failures

Idempotency

duplicate-safe business effects

RequirementBest starting patternWhyCommon mistake
One background job -> one workerWork queueSimple ownership + horizontal worker scalingUsing pub-sub and accidentally doing the job multiple times
Many services react to one factPub/SubEach subscriber has independent progressPutting consumers in one competing queue so only one sees the event
Replay historical eventsEvent stream/logRetention + offsetsUsing a destructive task queue and discovering history is gone
Same entity must stay orderedFIFO group / keyed partitionOrder only where business needs itDemanding global ordering and destroying throughput
Run work laterDelayed queueNo repeated DB pollingAssuming expiry is exact and ignoring race with success
Temporary failureRetry queueBackoff gives dependency time to recoverImmediate infinite retries
Permanent/poison failureDLQIsolate + inspect + controlled replayTreating 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

Publish / enqueue

orders.queue

M1 • M2 • M3

each message -> one worker

Worker 1

Processes M1

Worker 2

Processes M2

Worker 3

Processes M3

Best when

One job should be owned by one worker.
You want background processing and horizontal worker scaling.
Temporary traffic spikes should be buffered.
The producer does not need the final result immediately.

Avoid / reconsider when

Many independent services must each receive the same message.
You need replay of historical events for new consumers.
Strict global ordering is more important than parallel processing.

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

Email jobsImage processingInvoice generationOrder fulfilment jobsPayment reconciliation

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

Say 'one message -> one successful worker' rather than 'one queue can have only one consumer'. Multiple consumers can compete for the same queue.

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

Publish / enqueue

transcode.jobs

buffered work

competing workers

Worker A

1080p

Worker B

720p

Worker C

480p

Best when

CPU-heavy or I/O-heavy background jobs
Jobs are independent
Workers can be scaled horizontally
The user does not need synchronous completion

Avoid / reconsider when

Every downstream system needs a copy
A single job must be processed by several stages with different semantics without explicit workflow design
Work depends on strict cross-job ordering

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

Video transcodingPDF generationThumbnail generationML inference jobsBulk email sending

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

Mention prefetch/concurrency and poison-job handling. Scaling workers without protecting downstream systems can move the bottleneck instead of solving it.

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

Publish / enqueue

orders.created

topic / event bus

each subscriber receives the event

Notification

send confirmation

Analytics

record conversion

Warehouse

store event

Best when

Multiple systems react to the same business event
You want loose coupling between publisher and subscribers
Subscribers can fail or scale independently
New subscribers may be added later

Avoid / reconsider when

Exactly one worker should own the task
The publisher requires an immediate response from every subscriber
The event contract is unstable and unmanaged

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

Order created eventsBooking confirmed eventsAudit pipelinesAnalyticsCache invalidation

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

Differentiate pub-sub from fanout implementation. Pub-sub is the communication model; fanout is one routing behavior that broadcasts to all bound consumers.

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

Publish / enqueue

fanout exchange

broadcast

broadcast copy

Service A

refresh config

Service B

refresh config

Service C

refresh config

Best when

All subscribers truly need every event
Broadcasting configuration or cache invalidation
Independent audit/notification pipelines

Avoid / reconsider when

Most consumers only need a small subset
Subscriber count is huge and payloads are large
Selective routing would dramatically reduce traffic

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

Cache invalidationSystem-wide domain eventsAudit copiesBroadcast notifications

Main benefit

Very simple broadcast semantics.

Main trade-off

Network/storage cost grows with the number of subscribers.

Interview answer

Use fanout only when broadcast is intended. Do not send every event to every service and make consumers discard 99% of traffic.

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

Publish / enqueue

payments exchange

topic routing

routing-key match

Booking Queue

payment.success

Fraud Queue

payment.*

Failure Queue

payment.failed

Best when

Consumers need subsets of events
Event families share a broker
Routing rules are stable and meaningful

Avoid / reconsider when

Every consumer needs every message
Routing logic becomes so complicated that it hides business workflow
Topics are being used as a replacement for authorization

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

payments.*orders.createdusers.registeredregion.in.*inventory.low

Main benefit

Reduces unnecessary deliveries while keeping publishers decoupled.

Main trade-off

Topic and schema governance become important at scale.

Interview answer

Give a concrete key such as payment.success and show which services subscribe to it instead of defining topic routing abstractly.

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

Publish / enqueue

Partition / FIFO Group

CREATED -> PAID -> SHIPPED

ordered consumption

Order Consumer

applies sequence

Best when

State transitions must be applied in order
Per-account ledger events
Inventory mutations for the same SKU
Workflow steps for one entity

Avoid / reconsider when

Jobs are independent
Throughput matters more than ordering
You are demanding global ordering without a business requirement

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

Order lifecycleAccount eventsSeat state transitionsPer-SKU inventory updates

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

Ask 'ordering for what?' Per-order ordering is usually enough; global ordering for every order in the company is rarely required.

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

Publish / enqueue

Priority Scheduler

P1 first, fairness applied

priority-aware dispatch

Critical Worker

P1 security

Normal Worker

P2 payments

Bulk Worker

P3 marketing

Best when

Business urgency differs significantly
SLA classes exist
Critical incidents must bypass bulk jobs

Avoid / reconsider when

Everything is marked high priority
Fairness is required
Priority adds complexity without an actual SLA difference

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

Security alertsPayment incidentsPremium customer jobsUrgent notifications

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

Discuss starvation. A priority queue answer is incomplete without explaining how low-priority work eventually progresses.

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

Publish / enqueue

Delayed Queue

hidden until due time

after delay

Expiry Worker

HELD -> AVAILABLE if still unpaid

Best when

Short/medium future actions
Booking or lock expiry
Retry after backoff
Reminder workflows

Avoid / reconsider when

You need sub-millisecond timer precision
Jobs are scheduled months/years out and broker retention/cost is unsuitable
The queue technology does not support delay semantics cleanly

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

Release seat hold in 5 minutesSend reminder tomorrowRetry in 30 secondsCart abandonment email

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

In booking systems, explain the race: payment success may arrive at the same time as seat-expiry. The consumer must check authoritative booking state before releasing the seat.

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

Publish / enqueue

Retry Tier

5s -> 30s -> 5m + jitter

bounded retry

Consumer Retry

success -> ACK

DLQ

max attempts

Best when

Temporary downstream failure
Recoverable timeout
Provider rate limiting with retry guidance
Transient database/network issue

Avoid / reconsider when

Invalid payload
Business-rule rejection
Permanent authentication/authorization failure
Side effect is not idempotent

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

Email provider timeoutPayment callback handlingWebhook deliveryTemporary DB outage

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

Classify errors before retrying. 'Retry everything 3 times' is not a robust strategy.

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

Publish / enqueue

DLQ

payload + reason + attempts

investigate before replay

Alerting

page/notify owner

Replay Tool

fix -> controlled replay

Best when

Poison messages must be isolated
You need operator visibility
You need controlled replay after fixing bugs

Avoid / reconsider when

Used as a substitute for validation
Nobody owns monitoring/replay
Sensitive payloads are stored without correct retention/security controls

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

Schema mismatchRepeated provider failureUnexpected dataBuggy consumer version

Main benefit

Bad messages stop poisoning the normal processing path.

Main trade-off

An unmonitored DLQ becomes a silent data-loss graveyard.

Interview answer

Always mention alerting + replay strategy. Saying 'after 3 retries move to DLQ' is only half the 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

Publish / enqueue

report.request

replyTo=report.reply

correlated reply

Report Worker

publishes correlated result

Reply Queue

req-42 response

Best when

Messaging is the required transport
Operation may survive requester reconnection
You need queued requests but eventual responses

Avoid / reconsider when

Simple low-latency RPC is enough
Caller cannot tolerate asynchronous timeout semantics
Correlation state would add unnecessary complexity

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

Long-running report generationLegacy broker integrationsDevice/edge communicationAsync command status

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

Do not force request-reply into an architecture where plain HTTP/gRPC would be clearer. Explain why queued transport is necessary.

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

Publish / enqueue

orders topic

P0 | P1 | P2 + retained offsets

independent consumer groups

Analytics Group

own offsets

Notification Group

own offsets

Warehouse Group

replay/history

Best when

Replay matters
Many independent consumers
High-throughput event pipelines
CDC/analytics/event sourcing
Long-lived domain events

Avoid / reconsider when

You only need a simple small task queue
Operating partitions/offsets/retention is unnecessary complexity
Per-message rich routing is the dominant requirement

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

Kafka pipelinesCDCActivity streamsAnalyticsEvent sourcingData lake ingestion

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

The key difference is retained log + independent offsets/replay, not simply 'Kafka is faster than RabbitMQ'.

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 Group: Notification

Consumer 1

P0

Consumer 2

P1

Consumer 3

P2

Important scaling rule

If a topic has 3 partitions, one consumer group can normally process at most 3 partitions in parallel. Adding 10 consumers to the same group does not create 10-way parallelism for only 3 partitions.

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

A broker may offer exactly-once guarantees within part of its own processing model, but that does not magically make an external payment API, email provider and database transaction execute exactly once together.

Correctness

Message Ordering

Ordering requirements directly affect partitioning and parallelism.

Global Ordering

M1
M2
M3
M4
M5

Every message needs one global order. This often limits parallelism.

Per-Key Ordering

order-1:
CREATED -> PAID -> SHIPPED

order-2:
CREATED -> CANCELLED

Often enough in real systems. Events for the same entity are routed to the same partition.

Partition by Order ID
partition =
    hash(orderId)
    % partitionCount;


// Same orderId
// -> same partition
// -> ordered processing

Prefer the narrowest ordering guarantee that solves the business problem

You often do not need every order in the company globally ordered. You usually need events for the same order to remain ordered.

Message Lifecycle

Acknowledgements & Visibility Timeout

The broker needs a way to determine whether processing completed successfully.

Queue

Message available

Consumer receives message

Consumer

Processing...

ACK

Message Complete

Remove / commit progress

What if Consumer Crashes Before ACK?

Consumer A

Gets M1

Crash

No ACK

Message is not considered complete

Visible again / reassigned

Consumer B

Processes M1 again

Visibility timeout

In queue systems that use visibility timeouts, a received message becomes temporarily invisible to other consumers. If it is not successfully deleted or acknowledged in time, it becomes available again.

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

Deduplicate by Event ID
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.

Exponential Backoff
Attempt 1 -> immediately

Attempt 2 -> 2 seconds

Attempt 3 -> 4 seconds

Attempt 4 -> 8 seconds

Attempt 5 -> 16 seconds

Then -> DLQ
Backoff + Jitter
delay =
    min(
        MAX_DELAY,
        BASE_DELAY * (2 ** retryCount)
    );

jitter =
    random(0, 1000);

nextRetry =
    delay + jitter;

Why jitter?

If 100,000 messages fail at the same time and all retry after exactly 30 seconds, they can overload the recovering dependency again. Jitter spreads retries across time.

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 only process 20K/sec

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

hash(key)

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

If one routing key produces a huge percentage of events, one partition can become overloaded while other partitions remain mostly idle.

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

Replicate

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

partitioner selects partition

Partition 0

leader + replicas • offsets 0..N

Partition 1

leader + replicas • offsets 0..N

Partition 2

leader + replicas • offsets 0..N

consumer group assignment

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

More partitions can increase parallelism, but they also increase metadata, rebalance work and operational complexity. Changing partition count can also change hash-to-partition mapping for future events. Choose enough partitions for expected throughput and ordering keys instead of treating partition count as an arbitrary performance knob.

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

publish

Exchange

direct • topic • fanout • headers

bindings decide destination queues

booking.queue

payment.success

fraud.queue

payment.*

audit.queue

all payment events

consumers + ACK / NACK

Workers

prefetch controls in-flight messages

ExchangeRouting ruleGood forExample
DirectExact routing-key matchSimple explicit routingpayment.success -> booking.queue
TopicWildcard patternsEvent familiespayment.* or order.#
FanoutBroadcast to every bindingAll subscribers need every messageconfig.changed -> all services
HeadersHeader-based matchingSpecial metadata routingx-region=IN + x-tier=premium

Prefetch is a throughput/fairness control

A consumer with prefetch 100 may hold up to 100 unacknowledged messages. That can improve throughput for fast handlers, but it can also make distribution unfair and increase redelivery after crashes. Tune prefetch to message cost and worker concurrency.

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.

FeatureKafka-style LogRabbitMQ-style BrokerManaged Queue
Core modelDistributed event logMessage broker / queuesManaged queue service
ReplayStrong fitUsually queue-consumption orientedService dependent
RoutingTopics + partitionsRich exchanges / routingQueues + topics depending on service
High-throughput streamsExcellent fitPossible, but not its only strengthDepends on managed service
Task queuePossibleVery naturalVery natural
Operational effortCan be significantCan be significantProvider handles more infrastructure
Typical useEvents, CDC, streams, analytics pipelinesJobs, routing, business messagingCloud-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

Replayable retained log
High throughput
Partition ordering
Independent consumer groups
Strong ecosystem for CDC/streaming

Trade-offs

Partition planning
Consumer lag/offset operations
Schema governance
Cluster/managed-service complexity

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

Direct/topic/fanout routing
Natural task queues
ACK/prefetch controls
Mature business messaging semantics

Trade-offs

Replay is not its core model
Queue topology needs design
Large retained event-history workloads may fit logs better

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

Low operational burden
Elastic scaling
Visibility timeout
DLQ/redrive
Cloud integration

Trade-offs

Provider semantics/limits
Less control over internals
Cross-cloud portability
Feature set differs by provider

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

Independent subscriptions
Elastic fanout
Push/pull options depending on provider
Low broker operations

Trade-offs

Provider-specific filtering/ordering
At-least-once duplicates
Cost can grow with fanout/egress

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

1. Save order

Database

COMMIT succeeds

2. Publish event

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

Outbox worker / CDC

Message Broker

ORDER_CREATED

Why it helps

The business state and intent to publish are committed in the same local database transaction. A worker can safely retry publishing the outbox event.

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

booking-confirmed.json
{
  "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

BookingEventPublisher.java
@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

BookingNotificationConsumer.java
@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

A database transaction can atomically store the processed-event marker and your local state, but it cannot automatically include an external email/payment provider. For external APIs, use provider idempotency keys where available or store an outbox/job record and make the external call retry-safe.

4. Retry Classification

RetryPolicy.java
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

Outbox publishing flow
// 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

The user must immediately know whether Seat A10 was acquired. Use an atomic lock, conditional database update or transaction for that critical decision. Queues are used around the booking flow for expiry, payment events, notifications, ticket generation, analytics and other asynchronous work.

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

POST /seat-holds

Seat Lock Service

atomic conditional acquire

authoritative write

Seat / Booking Store

AVAILABLE -> HELD, expiresAt=T+5m

immediate response

Hold Created

bookingId + expiry

Atomic seat hold idea
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 it

3. 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

schedule T+5m

booking.expiry

delayed queue

5 minutes later

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

both may arrive concurrently

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.

Booking confirmation transaction
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

publish once available

booking.events

retained topic / pub-sub

independent consumer groups

Notification

email / SMS / push

Ticket Worker

generate QR / PDF

Analytics

conversion metrics

Warehouse

event pipeline

Why this boundary works

Seat ownership and booking confirmation stay on the synchronous correctness path. Slow or failure-prone side effects move behind durable queues, so an email outage cannot make a successful booking fail and analytics slowness cannot block the user.

7. Notification Retry and DLQ

Notification Consumer

email provider timeout

transient failure

Retry Topic / Queue

5s -> 30s -> 5m + jitter

max attempts

notification.dlq

alert + inspect + replay

8. Which Parts Should Be Sync vs Async?

OperationSync / AsyncReasonQueue pattern
Search showsSyncUser needs immediate resultNone
Acquire seat lockSyncImmediate correctness decisionNone for the lock itself
Create payment intentUsually SyncUser needs payment flow nowEvents may follow
Payment callback processingDurable + idempotentMust survive retries/duplicatesEvent/queue can decouple downstream work
Seat-hold expiryAsync delayedFuture action at T+NDelayed queue
Ticket generationAsyncCan finish after booking confirmationWork queue
Email/SMSAsyncProvider slowness must not fail bookingWork queue + retry + DLQ
AnalyticsAsyncEventually consistent side effectPub/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
Consumer Lag
Oldest Message Age
Messages/sec
Consumer Throughput
Retry Rate
DLQ Size
Processing Latency
ACK Failures
Broker Disk
Broker CPU
Partition Skew

Queue depth alone is not enough

A queue with one million tiny messages may be healthier than a queue containing 100 expensive jobs that have been stuck for hours. Monitor age, throughput and processing latency as well.

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.

1

Fundamentals

Why do we use a message queue in system design?

+
A queue decouples producers from consumers, lets work happen asynchronously, buffers traffic bursts, isolates temporary consumer failures and lets producers/consumers scale independently. The trade-off is eventual consistency plus new concerns such as retries, duplicates, ordering, monitoring and DLQs.
2

Fundamentals

When should I NOT use a message queue?

+
Do not add a queue when the caller needs an immediate authoritative response and a synchronous API is simpler, when the workload is tiny and operational complexity outweighs benefit, or when delayed/eventual processing would violate business semantics. A queue is an architectural boundary, not a default for every service call.
3

Patterns

Queue vs pub-sub — what is the difference?

+
A work queue normally delivers one job to one successful consumer from a competing worker pool. Pub-sub lets multiple independent subscriptions each receive the same event. Use a queue for 'do this job once'; use pub-sub for 'this happened and several systems may react'.
4

Patterns

Fanout vs topic routing?

+
Fanout broadcasts every message to every bound subscriber. Topic routing uses a key/pattern such as payment.success or payment.* so only interested subscribers receive it. Fanout is simpler; topic routing reduces unnecessary traffic and is better when subscriber interests differ.
5

Patterns

FIFO vs standard queue — which is better?

+
Neither is universally better. Choose FIFO only when business correctness requires ordering for a key or group. Standard queues usually allow greater throughput and parallelism. In interviews, first identify the ordering scope: per-order or per-account ordering is commonly enough, while global ordering is expensive.
6

Reliability

At-most-once vs at-least-once vs exactly-once?

+
At-most-once may lose messages but avoids broker-level redelivery. At-least-once retries, reducing loss but allowing duplicates, so consumers must be idempotent. Exactly-once must be defined carefully: a broker may provide exactly-once processing within its own scope, but external databases, payment APIs and emails still require transactions/idempotency to produce one business effect.
7

Reliability

Why can duplicate delivery happen even after a consumer successfully processes a message?

+
The consumer may commit the database transaction and then crash before sending the ACK. The broker cannot know the business side effect succeeded, so it redelivers. This is why processing and deduplication/idempotency must be designed together.
8

Reliability

How do you make a consumer idempotent?

+
Use a stable eventId or business idempotency key, enforce a unique database constraint, keep a processed-events table, perform deduplication in the same database transaction as the business update, and make external side effects use provider idempotency keys when available.
9

Reliability

What is a poison message?

+
A poison message consistently fails because of invalid data, incompatible schema or a deterministic code path. Retrying forever wastes capacity. After classification/bounded retries, move it to a DLQ with failure metadata and alert the owning team.
10

Reliability

How should retries be designed?

+
Retry only transient failures; use bounded attempts, exponential backoff and jitter. Separate retry traffic from fresh traffic when necessary, preserve attempt count and last error, and move exhausted/permanent failures to a DLQ. Never immediately retry a failing dependency in a tight loop.
11

Reliability

What is a visibility timeout?

+
In queue systems such as SQS-style services, receiving a message temporarily hides it from other consumers. If the consumer deletes/ACKs it before the timeout, processing is complete. If the consumer crashes or the timeout expires, the message becomes visible again and may be processed by another worker.
12

Ordering

How do you preserve order without destroying throughput?

+
Use per-key ordering. Hash orderId/accountId/bookingId to a partition or FIFO message group. Events for the same entity remain sequential while different keys are processed in parallel. Avoid global ordering unless it is truly required.
13

Ordering

What happens when one message in an ordered stream keeps failing?

+
If later messages must not pass it, that partition/group can become blocked. You need bounded retry, quarantine/DLQ policy and a business decision about whether later messages may proceed. This is one reason ordered systems need explicit poison-message handling.
14

Scaling

What is backpressure?

+
Backpressure appears when producers create work faster than consumers and the backlog grows. Handle it with consumer autoscaling, producer throttling, batching, admission control, prioritization, load shedding for non-critical work and downstream protection. A queue can absorb bursts but cannot absorb infinite sustained overload.
15

Scaling

Which metric is more useful: queue depth or oldest-message age?

+
Both matter. Queue depth tells you backlog size, while oldest-message age tells you user-visible delay/SLA impact. Also monitor production rate, consumption rate, processing latency, retry rate, DLQ rate and downstream saturation.
16

Kafka

Why does Kafka use partitions?

+
Partitions split a topic into parallel ordered logs. They provide horizontal throughput, distribute storage and let consumers in one group process different partitions concurrently. Ordering is guaranteed within a partition, so partition-key choice directly affects correctness and load distribution.
17

Kafka

What happens if a consumer group has 10 consumers but the topic has 3 partitions?

+
At most three consumers can actively process those three partitions at one time in that group; the remaining consumers are idle or standby. To increase parallelism, increase partition count or redesign the workload—but adding partitions has ordering/key-distribution consequences.
18

Kafka

What is consumer lag?

+
Lag is the distance between the latest available offsets and the offsets a consumer group has processed/committed. Rising lag means the group is falling behind. Diagnose processing latency, downstream bottlenecks, insufficient consumers, hot partitions or failures.
19

Kafka

How do you choose a Kafka partition key?

+
Choose a stable key that groups events requiring order, such as orderId. It should also have high enough cardinality to distribute load. A poor key such as one celebrityId or one region can create a hot partition even when other partitions are idle.
20

RabbitMQ

Direct vs topic vs fanout exchange?

+
Direct routes when the routing key exactly matches a binding key. Topic supports wildcard patterns such as payment.*. Fanout ignores routing keys and broadcasts to all bound queues. Choose based on how selective routing must be.
21

RabbitMQ

What is prefetch and why does it matter?

+
Prefetch limits how many unacknowledged messages a consumer can hold. Too high can create unfair distribution and large redelivery after a crash; too low can underutilize fast workers because they repeatedly wait for the broker. Tune it to processing cost and concurrency.
22

Managed Queue

When would you choose SQS-style managed queues instead of Kafka?

+
Choose managed queues for durable async jobs when you value low operational overhead, visibility timeouts, elastic scaling and simple task ownership. Choose Kafka-style logs when replayable retained streams, many independent consumer groups, partitioned ordering and event-history processing are central.
23

Architecture

What is the dual-write problem?

+
If a service commits a database update and publishes an event as two separate operations, one can succeed while the other fails. The transactional outbox solves this by storing the business row and an outbox row in one DB transaction, then publishing the outbox asynchronously through a worker or CDC.
24

Architecture

What is the transactional inbox pattern?

+
An inbox stores incoming event IDs/messages in the consumer's database transaction before or while applying business state. It provides durable deduplication and makes at-least-once delivery easier to handle when the same event is received again.
25

Architecture

Command vs event — what should go on the queue?

+
A command asks a specific capability to do something, such as GenerateInvoice. An event states a fact that already happened, such as InvoiceGenerated. Commands usually have one logical owner; events may have many independent consumers. Keeping this distinction makes event-driven systems easier to reason about.
26

BookMyShow

Should BookMyShow put seat locking behind a queue?

+
Usually no for the user-facing lock decision. The user needs an immediate answer on whether the seat was acquired, so use an atomic lock/conditional database operation synchronously. Queues are excellent after that critical decision for expiry timers, ticket generation, notifications, analytics and other asynchronous side effects.
27

BookMyShow

Does a queue prevent double booking?

+
Not by itself. Double booking is prevented by authoritative concurrency control such as an atomic seat lock, conditional update, unique constraint or transaction. A queue can serialize some workflows, but correctness should not depend on assuming messages never duplicate or arrive late.
28

BookMyShow

How would you release a seat after the payment window expires?

+
Publish a delayed expiry event containing bookingId/seatId/holdVersion. At expiry, the worker reads authoritative booking state and releases only if the booking is still HELD/PENDING and the hold version matches. If payment already succeeded, the expiry becomes a no-op. This makes the race safe and idempotent.
29

Scenario

How would you send 10 million emails without overloading the provider?

+
Write email jobs to a durable work queue, partition/categorize if needed, run workers with concurrency and provider rate limits, use batching where supported, retry transient failures with backoff+jitter, put permanent failures in a DLQ, track idempotency and monitor queue age, success rate and provider throttling.
30

Scenario

A queue backlog is growing even after doubling workers. What do you check?

+
Compare arrival vs processing rate, inspect oldest-message age, worker CPU, external API/DB saturation, lock contention, hot partitions, per-message latency, retry storms and poison messages. If the bottleneck is the database or provider, adding more workers can make throughput worse.
31

Scenario

How do you safely replay a DLQ?

+
First fix or classify the root cause, validate schema/version compatibility, choose a bounded subset, replay at a controlled rate through the normal idempotent handler, monitor downstream capacity and keep audit metadata. Never bulk-replay blindly into the same failing dependency.
32

Scenario

How do you evolve message schemas without breaking consumers?

+
Prefer backward-compatible additions, version events, avoid silently changing field meaning, use schema validation/registry where appropriate, support old/new versions during migration, deploy consumers before producers for compatible changes and define ownership/deprecation rules.

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.

Mastering System Design — HLD

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.

KafkaQueuesPub/SubRetryDLQCachingDatabaseBookMyShow

Continue Learning