Stage 4 · Provision
Asynchronous Systems
Queue Semantics
Visibility timeouts, acknowledgements, dead-letter queues, and retry policies in SQS and RabbitMQ.
Queue Fundamentals
Message queues decouple producers from consumers. Producers enqueue messages; consumers dequeue and process them. Queues provide buffering (absorb traffic spikes), persistence (survive crashes), and ordering (process in sequence). Understanding queue semantics is critical for building reliable async systems.
Visibility Timeout
The visibility timeout is the period after a message is received during which it is hidden from other consumers. If the consumer does not delete the message within this period (because it crashed or timed out), the message becomes visible again for redelivery.
1. Consumer receives message (visibility timeout starts: 30s)
2. Consumer processes message...
3. If processing completes within 30s:
→ Consumer deletes message → Done
4. If processing takes > 30s:
→ Message becomes visible again
→ Another consumer receives the SAME message
→ Duplicate processing occurs
Solution: Extend visibility timeout during processingSet the visibility timeout longer than maximum expected processing time. Extend it periodically during long-running processing.
Acknowledgements
Acknowledgements confirm that a message has been successfully processed. Without acknowledgement, the message broker assumes the consumer failed and redelivers. There are two acknowledgement models: explicit delete (SQS) and ack/nack (RabbitMQ).
import boto3
sqs = boto3.client('sqs')
def process_messages():
while True:
response = sqs.receive_message(
QueueUrl=QUEUE_URL,
MaxNumberOfMessages=10,
WaitTimeSeconds=20,
VisibilityTimeout=300
)
for message in response.get('Messages', []):
try:
handle_message(message)
# Delete only after successful processing
sqs.delete_message(
QueueUrl=QUEUE_URL,
ReceiptHandle=message['ReceiptHandle']
)
except Exception as e:
# Message will become visible again after visibility timeout
log.error(f"Failed: {e}")Never delete a message before processing completes. If processing fails, the message returns to the queue for retry.
Dead-Letter Queues
A dead-letter queue (DLQ) captures messages that fail processing after a configured number of retries. Without a DLQ, poison messages block the queue indefinitely. The DLQ allows you to inspect failed messages and retry or discard them.
redrivePolicy:
deadLetterTargetArn: arn:aws:sqs:us-east-1:123456789:my-dlq
maxReceiveCount: 3 # After 3 failures, move to DLQAfter a message fails 3 times, it moves to the DLQ. This prevents poison messages from blocking the main queue while preserving failed messages for investigation.
Retry Policies
- Fixed delay — Retry after the same interval (5s, 5s, 5s). Simple but not adaptive.
- Exponential backoff — Double the delay each retry (1s, 2s, 4s, 8s). Standard approach.
- Exponential with jitter — Add randomness to prevent thundering herd.
- Custom backoff — Business-specific delays based on error type.
SQS vs RabbitMQ Semantics
| Feature | SQS | RabbitMQ |
|---|---|---|
| Visibility timeout | Configurable per message | Not applicable |
| Acknowledgement | Delete message to ack | Basic.ack / Basic.nack |
| Dead-letter queue | Built-in redrive policy | Exchange routing to DLQ |
| Retry handling | Visibility timeout expiry | Requeue with nack |
| Ordering | FIFO queue option | Queue-level ordering |
Every queue should have a dead-letter queue. Monitor the DLQ for poison messages. Alert when messages arrive in the DLQ. Investigate and fix the root cause, then replay messages from the DLQ.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.