Message Queues: Buffering Work Between Producers and Consumers
📑 On this page
- A concrete example: order confirmation email
- Producers, brokers, and consumers
- Buffering bursts
- Decoupling availability
- Durable messages
- Acknowledgment
- At-most-once delivery
- At-least-once delivery
- Exactly-once language
- Visibility timeout
- Ordering
- Partitioning
- Poison messages
- Dead-letter queues
- Delayed delivery
- Message size
- Message schema
- The transactional outbox
- Backpressure and admission control
- Observability
- Knowledge check
- The one idea to remember
A producer and a worker do not always operate at the same speed.
Checkout may create hundreds of orders in one minute while an email provider can send only a smaller steady rate. Making every customer wait for the email service would connect checkout reliability to a nonessential dependency.
A message queue stores work between producers and consumers so they can operate at different times and rates.
The queue creates a durable boundary, but it also introduces delivery, ordering, retry, and backlog responsibilities.
A concrete example: order confirmation email
After committing an order, the order service enqueues:
SendOrderConfirmation {
orderId: 8472,
customerId: 219
}A worker later:
- receives the message,
- loads current order data,
- sends the email,
- records success,
- and acknowledges the message.
Checkout can finish without waiting for the email provider.
Producers, brokers, and consumers
A queue system has three broad roles:
- producer creates a message,
- broker stores and delivers it,
- consumer receives and processes it.
One application can play several roles for different queues.
The broker coordinates delivery but does not understand all business correctness.
Buffering bursts
If producers briefly create 1,000 jobs per second and consumers handle 600, the queue absorbs the difference.
When the burst ends, consumers catch up. This works only when:
- the queue has capacity,
- backlog age remains acceptable,
- and average processing capacity eventually exceeds average arrival rate.
A sustained mismatch creates an ever-growing delay.
Decoupling availability
A producer can continue accepting work while a consumer restarts or its dependency is unavailable.
This improves availability when delayed completion is acceptable. It does not help if the producer cannot safely acknowledge the user until the consumer's result exists.
Decoupling timing is a business decision as much as an architecture choice.
Durable messages
A durable queue persists messages across broker or process restart according to its guarantee.
Durability may depend on:
- replication,
- publisher confirmation,
- storage settings,
- and acknowledgment mode.
Sending a message without waiting for broker confirmation can lose work before it is durably accepted.
Acknowledgment
A consumer acknowledges after processing succeeds.
If it crashes before acknowledgment, the broker can redeliver the message. If it acknowledges before applying the effect, a later crash can lose the work.
The usual order favors at-least-once delivery and requires idempotent consumers.
At-most-once delivery
At-most-once means a message is delivered zero or one time.
It avoids duplicate processing but can lose work if failure occurs after removal and before completion. This may suit low-value telemetry where occasional loss is acceptable.
It is dangerous for critical business actions without another recovery source.
At-least-once delivery
At-least-once delivery retries until acknowledgment or policy exhaustion.
It reduces silent loss but permits duplicates. A consumer must recognize that:
- the same message can arrive twice,
- processing may have succeeded before a crash,
- and acknowledgments can be lost.
Idempotency is part of the consumer contract.
Exactly-once language
Some platforms provide exactly-once behavior within a defined scope.
Ask:
- exactly once at which layer?
- for message delivery or business effect?
- within one broker transaction or across an external API?
- under which configuration and failure cases?
End-to-end exactly-once outcomes usually rely on transactional state and deduplication, not transport magic alone.
Visibility timeout
In some queues, receiving a message hides it temporarily rather than deleting it.
The visibility timeout must exceed normal processing time or be extended while work continues. If it expires too early, another worker receives the same job concurrently.
If it is too long, a crashed worker delays recovery.
Ordering
Queues may preserve order only:
- within one partition,
- for one message group,
- under one producer,
- or until retries occur.
Parallel consumers naturally finish at different times. If business correctness requires per-account ordering, use partition keys, sequence checks, or versioned updates.
Global ordering is expensive and often unnecessary.
Partitioning
High-throughput queues divide messages among partitions.
A partition key controls placement. Messages with the same key can preserve local order, while different keys process in parallel.
A poor key can create a hot partition, such as sending all traffic for one large customer to one shard.
Poison messages
A poison message always fails because its content or required state is invalid.
Without limits, it can be retried forever and consume worker capacity. Policies should:
- count attempts,
- delay retries,
- capture failure reason,
- move exhausted messages aside,
- and alert an owner.
Healthy messages should continue.
Dead-letter queues
A dead-letter queue stores messages that exceeded retry policy or could not be delivered.
It is not a trash can. Teams need a process to:
- inspect,
- classify,
- correct underlying causes,
- replay safely,
- or discard with an audit record.
An unnoticed dead-letter queue is hidden data loss.
Delayed delivery
Some queues can make a message available later.
Uses include:
- retry backoff,
- reminders,
- temporary holds,
- and scheduled checks.
Long-term scheduling may need a dedicated scheduler because queue retention and timing precision can be limited.
Message size
Queues work best with modest messages.
Large videos, reports, or documents should usually live in object storage, while the message carries:
- identifier,
- location,
- checksum,
- and metadata.
Small messages reduce broker pressure and retry cost.
Message schema
Producers and consumers need a versioned contract.
Safe evolution often:
- adds optional fields,
- preserves old meaning,
- tolerates unknown fields,
- and tracks schema version when necessary.
A message may remain in backlog while new code deploys, so consumers must handle older formats.
The transactional outbox
A service may need to update its database and publish a message.
Doing these separately creates two failure windows:
- database commits but message is not sent,
- message is sent but database rolls back.
The outbox pattern writes the business change and an outgoing-message record in one local transaction. A publisher later sends the outbox record reliably.
Backpressure and admission control
A queue is finite.
When backlog exceeds safe age or storage, the system may:
- slow producers,
- reject low-priority work,
- shed optional jobs,
- add consumers,
- or pause upstream imports.
Monitoring only queue length misses whether old messages are waiting too long.
Observability
Measure:
- incoming rate,
- completion rate,
- oldest-message age,
- processing duration,
- attempts,
- acknowledgment failures,
- partition imbalance,
- and dead-letter volume.
Trace identifiers connect a user action to its eventual worker outcome.
Knowledge check
- How does a queue let producers and consumers operate at different speeds?
- Why does at-least-once delivery require idempotent processing?
- What happens when visibility timeout is too short?
- Why is a dead-letter queue not a complete failure solution?
- Which inconsistency does the transactional outbox prevent?
The one idea to remember
Queues buffer and decouple work, allowing producers and consumers to operate independently. Reliability still depends on durable acceptance, acknowledgment timing, idempotency, ordering rules, schema evolution, backlog control, and active handling of failed messages.