Azure Messaging
Choose the right Azure messaging service and delivery semantics for reliable, decoupled, event-driven systems.
Handle retries, failures, and exactly-once processing.
Use This Skill When
- The user needs asynchronous processing design (commands, events, streams)
- The user asks about retries, dead-lettering, idempotency, or ordering
- The user needs to decouple systems via events or messaging
- The user needs high-throughput streaming or reactive patterns
Context: Messaging Maturity
Immature: Synchronous API calls, no retries, tightly coupled
Developing: Simple queues created, basic retry logic
Managed: Service Bus/Event Hub with DLQ, idempotency, monitoring → Target
Optimized: Saga pattern for distributed transactions, stream processing, real-time analytics
Required Inputs
- Message pattern: Command (request-response), Event (pub-sub), Stream (high-volume telemetry)
- Throughput: 100 msg/sec? 1M msg/sec?
- Latency: <1sec? Batch processing acceptable?
- Delivery guarantee: At most once? At least once? Exactly once?
- Ordering requirement: FIFO per partition? Or unordered?
- Consumer pattern: Single consumer? Multiple competing consumers? Fan-out?
- Scale: Number of producers, consumers, message size?
Decision Tree
What is the main use case?
├─ Simple queue (deferred processing) → Storage Queues (cheapest, simple)
├─ Enterprise queue (retries, DLQ, partitioning) → Service Bus Queue
├─ Publish-Subscribe (fan-out) → Service Bus Topic or Event Grid
├─ High-volume streaming (1000+ msg/sec) → Event Hubs
└─ Reactive events (webhooks, triggers) → Event Grid
What's the throughput target?
├─ <1000 msg/sec → Storage Queues or Service Bus
├─ 1000-10,000 msg/sec → Service Bus with partitions
├─ > 10,000 msg/sec → Event Hubs
└─ Needs low latency (<100ms) → Event Hubs or Service Bus Premium
Do you need ordering or exactly-once semantics?
├─ Yes, FIFO per partition → Event Hubs or Service Bus with sessions
├─ Yes, exactly-once → Service Bus with deduplication ID
└─ No, at-least-once acceptable → Any service + idempotency in handler
How many consumers need this message?
├─ One → Queue (Service Bus or Storage)
├─ Multiple (fan-out) → Service Bus Topic or Event Grid
└─ Streams (continuous analytics) → Event Hubs
Workflow
Phase 1: Service Selection
| Service |
Use Case |
Throughput |
Ordering |
Features |
| Storage Queues |
Simple, cheap |
<2K msg/sec |
No |
TTL, visibility timeout |
| Service Bus Queue |
Enterprise, retries |
Up to 2K msg/sec |
FIFO (sessions) |
DLQ, dedup, TTL, auth |
| Service Bus Topic |
Pub-Sub, fan-out |
Up to 2K msg/sec |
No |
Subscriptions, filters |
| Event Hubs |
Streaming, telemetry |
1M+ msg/sec |
Per partition |
Consumer groups, retention |
| Event Grid |
Reactive, webhooks |
Millions/sec |
No |
Built-in integrations |
Phase 2: Create Messaging Infrastructure
Service Bus Queue (for command processing):
az servicebus queue create \
--namespace-name "sb-${app}-${env}" \
--resource-group $RG \
--name "order-processing" \
--max-delivery-count 10 \
--duplicate-detection-history-time-window "PT5M" \
--dead-letter-on-filter-evaluation-error \
--dead-letter-on-message-expiration
# Max retries: 10, then DLQ
# Dedup window: 5 minutes (for idempotency)
# Auto DLQ on TTL expiration
Service Bus Topic (for event distribution):
az servicebus topic create \
--namespace-name "sb-${app}-${env}" \
--resource-group $RG \
--name "order-events" \
--enable-subscriptions true
# Create subscription (consumer group)
az servicebus topic subscription create \
--namespace-name "sb-${app}-${env}" \
--resource-group $RG \
--topic-name "order-events" \
--name "email-service" \
--max-delivery-count 3 \
--dead-letter-on-filter-evaluation-error
# Different consumers listen on same topic via subscriptions
Event Hubs (for streaming):
az eventhubs eventhub create \
--namespace-name "eh-${app}-${env}" \
--resource-group $RG \
--name "telemetry" \
--partition-count 4 \
--message-retention 1 \
--enable-capture true \
--capture-interval-in-seconds 300 \
--capture-size-limit-in-bytes 314572800
# 4 partitions for 4 concurrent consumers
# Capture to storage every 5 minutes or 300MB
Phase 3: Send Messages
Produce to Service Bus Queue:
// .NET
var client = new ServiceBusClient(connectionString);
var sender = client.CreateSender("order-processing");
var message = new ServiceBusMessage("Order 123");
message.MessageId = "order-123"; // For deduplication
message.TimeToLive = TimeSpan.FromMinutes(5); // Auto-DLQ if not processed
message.ApplicationProperties["OrderId"] = "123";
await sender.SendMessageAsync(message);
Publish to Event Hub:
// .NET
var producer = new EventHubProducerClient(connectionString, "telemetry");
var batch = await producer.CreateBatchAsync();
batch.TryAdd(new EventData(new BinaryData("temperature=72.5")));
batch.TryAdd(new EventData(new BinaryData("humidity=45%")));
await producer.SendAsync(batch);
Phase 4: Consume & Handle Failures
Consume with retry and DLQ:
// .NET
var client = new ServiceBusClient(connectionString);
var processor = client.CreateProcessor("order-processing");
processor.ProcessMessageAsync += async args => {
try {
var message = args.Message;
var orderId = message.ApplicationProperties["OrderId"];
// Idempotency check
if (await database.OrderProcessed(orderId)) {
await args.CompleteMessageAsync(message);
return;
}
// Process order
await ProcessOrder(orderId);
await args.CompleteMessageAsync(message);
} catch (Exception ex) {
// Retry automatically (up to max-delivery-count)
// After max retries, goes to DLQ
throw; // Nack, retry
}
};
processor.ProcessErrorAsync += args => {
Console.WriteLine($"Error: {args.Exception}");
return Task.CompletedTask;
};
await processor.StartProcessingAsync();
Monitor DLQ (failed messages):
# Peek at DLQ
az servicebus queue peek \
--namespace-name "sb-${app}-${env}" \
--resource-group $RG \
--name "order-processing/$DeadLetterQueue"
Phase 5: Ensure Exactly-Once Processing (Idempotency)
Deduplication ID (Service Bus):
Message A sent with MessageId="order-123"
├─ First delivery: Processed ✓
├─ Retry (within 5-min window): Not reprocessed (dedup)
└─ After 5 min: Can send again (different transaction)
Idempotency key in handler (Event Hub, Storage Queue):
# Python
def process_order(event):
order_id = event['OrderId']
# Check if already processed
if db.query(f"SELECT * FROM processed_orders WHERE order_id = {order_id}"):
return # Already done, idempotent
# Process
db.insert("orders", {"id": order_id, "status": "processing"})
# ... fulfill order ...
db.insert("processed_orders", {"order_id": order_id, "timestamp": now()})
Phase 6: Scaling & Partitioning
Service Bus (sharding via topics/queues):
Instead of: Single queue "orders" (bottleneck at scale)
Use: 10 queues: "orders-0", "orders-1", ..., "orders-9"
Producer hashes order_id % 10 → routes to specific queue
Consumers: One per queue for parallelism
Event Hubs (partitions):
# 4 partitions = 4 independent streams
# Each consumer group member gets 1 partition
# Max throughput: ~1M events/sec with 32 partitions
# Each partition maintains ordering FIFO per session_id
Phase 7: Monitoring & Alerting
Setup monitoring:
az monitor metrics list \
--resource-group $RG \
--resource-type "Microsoft.ServiceBus/namespaces" \
--resource-id "sb-${app}-${env}" \
--metric "ActiveMessageCount,DeadletteredMessageCount"
Alerts:
- Active message count > 10,000 (backlog building)
- Dead-lettered message count > 0 (failures happening)
- Consumer lag > 1 hour (consumer slow/stopped)
- Message age > TTL (about to expire)
Output Contract
Messaging Architecture
- Service chosen (Queue, Topic, Event Hub)
- Number of queues/topics, partitions
- Consumer count and groups
Delivery & Ordering Design
- Delivery guarantee: At most once, at least once, exactly once
- Ordering requirements (FIFO per partition? Global?)
- Idempotency strategy (dedup ID, business key check)
Failure Handling
- Max retries before DLQ
- DLQ monitoring and remediation process
- Message TTL (time to live)
Configuration & Access
- Connection strings or managed identity scopes
- Queue/topic names and subscription details
- Retention policy (1-30 days)
Operations Checklist
- Alerts configured for backlog, lag, DLQ
- DLQ monitoring and replay procedure
- Capacity planning (throughput targets)
Guardrails
- Always define a DLQ: Dead-lettered messages need handling (manual replay or archive).
- Implement idempotency: Assume messages arrive >1 time; handle gracefully.
- Use TTL (time-to-live): Prevent stale messages; auto-expire to DLQ.
- Monitor backlog: If active message count grows, consumer is slow.
- Partition for scale: Don't use single queue for >2K msg/sec.
- Log message content (redacted): Include order ID, not full message body (size/privacy).
- Test failure scenarios: What if consumer crashes mid-processing? Does DLQ work?
- Avoid coupling: Producers and consumers should not know each other.
1---2name: azure-messaging3description: Design asynchronous messaging patterns in Azure using Service Bus, Event Hubs, Event Grid, and Storage Queues. Use this skill when users ask for queueing, pub-sub, event streaming, retries, or decoupled system design. Covers delivery semantics, dead-lettering, idempotency, and monitoring.4---56# Azure Messaging78Choose the right Azure messaging service and delivery semantics for reliable, decoupled, event-driven systems.9Handle retries, failures, and exactly-once processing.1011## Use This Skill When1213- The user needs asynchronous processing design (commands, events, streams)14- The user asks about retries, dead-lettering, idempotency, or ordering15- The user needs to decouple systems via events or messaging16- The user needs high-throughput streaming or reactive patterns1718## Context: Messaging Maturity1920**Immature**: Synchronous API calls, no retries, tightly coupled 21**Developing**: Simple queues created, basic retry logic 22**Managed**: Service Bus/Event Hub with DLQ, idempotency, monitoring → **Target** 23**Optimized**: Saga pattern for distributed transactions, stream processing, real-time analytics2425## Required Inputs2627- **Message pattern**: Command (request-response), Event (pub-sub), Stream (high-volume telemetry)28- **Throughput**: 100 msg/sec? 1M msg/sec?29- **Latency**: <1sec? Batch processing acceptable?30- **Delivery guarantee**: At most once? At least once? Exactly once?31- **Ordering requirement**: FIFO per partition? Or unordered?32- **Consumer pattern**: Single consumer? Multiple competing consumers? Fan-out?33- **Scale**: Number of producers, consumers, message size?3435## Decision Tree3637```38What is the main use case?39├─ Simple queue (deferred processing) → Storage Queues (cheapest, simple)40├─ Enterprise queue (retries, DLQ, partitioning) → Service Bus Queue41├─ Publish-Subscribe (fan-out) → Service Bus Topic or Event Grid42├─ High-volume streaming (1000+ msg/sec) → Event Hubs43└─ Reactive events (webhooks, triggers) → Event Grid4445What's the throughput target?46├─ <1000 msg/sec → Storage Queues or Service Bus47├─ 1000-10,000 msg/sec → Service Bus with partitions48├─ > 10,000 msg/sec → Event Hubs49└─ Needs low latency (<100ms) → Event Hubs or Service Bus Premium5051Do you need ordering or exactly-once semantics?52├─ Yes, FIFO per partition → Event Hubs or Service Bus with sessions53├─ Yes, exactly-once → Service Bus with deduplication ID54└─ No, at-least-once acceptable → Any service + idempotency in handler5556How many consumers need this message?57├─ One → Queue (Service Bus or Storage)58├─ Multiple (fan-out) → Service Bus Topic or Event Grid59└─ Streams (continuous analytics) → Event Hubs60```6162## Workflow6364### Phase 1: Service Selection6566| Service | Use Case | Throughput | Ordering | Features |67|---------|----------|-----------|----------|----------|68| **Storage Queues** | Simple, cheap | <2K msg/sec | No | TTL, visibility timeout |69| **Service Bus Queue** | Enterprise, retries | Up to 2K msg/sec | FIFO (sessions) | DLQ, dedup, TTL, auth |70| **Service Bus Topic** | Pub-Sub, fan-out | Up to 2K msg/sec | No | Subscriptions, filters |71| **Event Hubs** | Streaming, telemetry | 1M+ msg/sec | Per partition | Consumer groups, retention |72| **Event Grid** | Reactive, webhooks | Millions/sec | No | Built-in integrations |7374### Phase 2: Create Messaging Infrastructure75761. **Service Bus Queue** (for command processing):77 ```bash78 az servicebus queue create \79 --namespace-name "sb-${app}-${env}" \80 --resource-group $RG \81 --name "order-processing" \82 --max-delivery-count 10 \83 --duplicate-detection-history-time-window "PT5M" \84 --dead-letter-on-filter-evaluation-error \85 --dead-letter-on-message-expiration86 87 # Max retries: 10, then DLQ88 # Dedup window: 5 minutes (for idempotency)89 # Auto DLQ on TTL expiration90 ```91922. **Service Bus Topic** (for event distribution):93 ```bash94 az servicebus topic create \95 --namespace-name "sb-${app}-${env}" \96 --resource-group $RG \97 --name "order-events" \98 --enable-subscriptions true99 100 # Create subscription (consumer group)101 az servicebus topic subscription create \102 --namespace-name "sb-${app}-${env}" \103 --resource-group $RG \104 --topic-name "order-events" \105 --name "email-service" \106 --max-delivery-count 3 \107 --dead-letter-on-filter-evaluation-error108 109 # Different consumers listen on same topic via subscriptions110 ```1111123. **Event Hubs** (for streaming):113 ```bash114 az eventhubs eventhub create \115 --namespace-name "eh-${app}-${env}" \116 --resource-group $RG \117 --name "telemetry" \118 --partition-count 4 \119 --message-retention 1 \120 --enable-capture true \121 --capture-interval-in-seconds 300 \122 --capture-size-limit-in-bytes 314572800123 124 # 4 partitions for 4 concurrent consumers125 # Capture to storage every 5 minutes or 300MB126 ```127128### Phase 3: Send Messages1291301. **Produce to Service Bus Queue**:131 ```csharp132 // .NET133 var client = new ServiceBusClient(connectionString);134 var sender = client.CreateSender("order-processing");135 136 var message = new ServiceBusMessage("Order 123");137 message.MessageId = "order-123"; // For deduplication138 message.TimeToLive = TimeSpan.FromMinutes(5); // Auto-DLQ if not processed139 message.ApplicationProperties["OrderId"] = "123";140 141 await sender.SendMessageAsync(message);142 ```1431442. **Publish to Event Hub**:145 ```csharp146 // .NET147 var producer = new EventHubProducerClient(connectionString, "telemetry");148 149 var batch = await producer.CreateBatchAsync();150 batch.TryAdd(new EventData(new BinaryData("temperature=72.5")));151 batch.TryAdd(new EventData(new BinaryData("humidity=45%")));152 153 await producer.SendAsync(batch);154 ```155156### Phase 4: Consume & Handle Failures1571581. **Consume with retry and DLQ**:159 ```csharp160 // .NET161 var client = new ServiceBusClient(connectionString);162 var processor = client.CreateProcessor("order-processing");163 164 processor.ProcessMessageAsync += async args => {165 try {166 var message = args.Message;167 var orderId = message.ApplicationProperties["OrderId"];168 169 // Idempotency check170 if (await database.OrderProcessed(orderId)) {171 await args.CompleteMessageAsync(message);172 return;173 }174 175 // Process order176 await ProcessOrder(orderId);177 await args.CompleteMessageAsync(message);178 } catch (Exception ex) {179 // Retry automatically (up to max-delivery-count)180 // After max retries, goes to DLQ181 throw; // Nack, retry182 }183 };184 185 processor.ProcessErrorAsync += args => {186 Console.WriteLine($"Error: {args.Exception}");187 return Task.CompletedTask;188 };189 190 await processor.StartProcessingAsync();191 ```1921932. **Monitor DLQ** (failed messages):194 ```bash195 # Peek at DLQ196 az servicebus queue peek \197 --namespace-name "sb-${app}-${env}" \198 --resource-group $RG \199 --name "order-processing/$DeadLetterQueue"200 ```201202### Phase 5: Ensure Exactly-Once Processing (Idempotency)2032041. **Deduplication ID** (Service Bus):205 ```206 Message A sent with MessageId="order-123"207 ├─ First delivery: Processed ✓208 ├─ Retry (within 5-min window): Not reprocessed (dedup)209 └─ After 5 min: Can send again (different transaction)210 ```2112122. **Idempotency key in handler** (Event Hub, Storage Queue):213 ```python214 # Python215 def process_order(event):216 order_id = event['OrderId']217 218 # Check if already processed219 if db.query(f"SELECT * FROM processed_orders WHERE order_id = {order_id}"):220 return # Already done, idempotent221 222 # Process223 db.insert("orders", {"id": order_id, "status": "processing"})224 # ... fulfill order ...225 db.insert("processed_orders", {"order_id": order_id, "timestamp": now()})226 ```227228### Phase 6: Scaling & Partitioning2292301. **Service Bus (sharding via topics/queues)**:231 ```232 Instead of: Single queue "orders" (bottleneck at scale)233 Use: 10 queues: "orders-0", "orders-1", ..., "orders-9"234 235 Producer hashes order_id % 10 → routes to specific queue236 Consumers: One per queue for parallelism237 ```2382392. **Event Hubs (partitions)**:240 ```bash241 # 4 partitions = 4 independent streams242 # Each consumer group member gets 1 partition243 # Max throughput: ~1M events/sec with 32 partitions244 245 # Each partition maintains ordering FIFO per session_id246 ```247248### Phase 7: Monitoring & Alerting2492501. **Setup monitoring**:251 ```bash252 az monitor metrics list \253 --resource-group $RG \254 --resource-type "Microsoft.ServiceBus/namespaces" \255 --resource-id "sb-${app}-${env}" \256 --metric "ActiveMessageCount,DeadletteredMessageCount"257 ```2582592. **Alerts**:260 - Active message count > 10,000 (backlog building)261 - Dead-lettered message count > 0 (failures happening)262 - Consumer lag > 1 hour (consumer slow/stopped)263 - Message age > TTL (about to expire)264265## Output Contract2662671. **Messaging Architecture**268 - Service chosen (Queue, Topic, Event Hub)269 - Number of queues/topics, partitions270 - Consumer count and groups2712722. **Delivery & Ordering Design**273 - Delivery guarantee: At most once, at least once, exactly once274 - Ordering requirements (FIFO per partition? Global?)275 - Idempotency strategy (dedup ID, business key check)2762773. **Failure Handling**278 - Max retries before DLQ279 - DLQ monitoring and remediation process280 - Message TTL (time to live)2812824. **Configuration & Access**283 - Connection strings or managed identity scopes284 - Queue/topic names and subscription details285 - Retention policy (1-30 days)2862875. **Operations Checklist**288 - Alerts configured for backlog, lag, DLQ289 - DLQ monitoring and replay procedure290 - Capacity planning (throughput targets)291292## Guardrails293294- **Always define a DLQ**: Dead-lettered messages need handling (manual replay or archive).295- **Implement idempotency**: Assume messages arrive >1 time; handle gracefully.296- **Use TTL (time-to-live)**: Prevent stale messages; auto-expire to DLQ.297- **Monitor backlog**: If active message count grows, consumer is slow.298- **Partition for scale**: Don't use single queue for >2K msg/sec.299- **Log message content (redacted)**: Include order ID, not full message body (size/privacy).300- **Test failure scenarios**: What if consumer crashes mid-processing? Does DLQ work?301- **Avoid coupling**: Producers and consumers should not know each other.