A queue is a shock absorber, not a pump
Putting a queue in front of a struggling service absorbs a spike but never adds throughput. Irina Scurtu works through the arithmetic of backlog and drain time with Azure Service Bus and Azure Functions, shows how depth-based scaling pushes the overload one hop downstream, and explains the TTL and dead-lettering settings that keep stale work from clogging the pipeline.
Key points
- A queue levels load but never adds throughput; drain time after a spike can exceed the spike itself
- Drain speed is set by headroom: a 240,000-message backlog clears in about 13 minutes at 300 msg/s of spare capacity
- If the average arrival rate stays above capacity the backlog never drains, and Service Bus eventually rejects sends with QuotaExceeded
- With depth-based scaling, instances times maxConcurrentCalls must stay below what the downstream can absorb
- In Azure Functions cap maxConcurrentCalls in host.json together with maximum-instance-count or functionAppScaleLimit
- Track message age with ServiceBusReceivedMessage.EnqueuedTime, and enable TimeToLive plus DeadLetteringOnMessageExpiration
Queue-Based Load Leveling smooths load; it does not shrink it. If consumers handle 600 messages a second and a ten-minute spike pushes 1,000, you end up with a 240,000-message backlog, the last arrival waits roughly 6m40s, and draining at 300 msg/s takes over 20 minutes — longer than the spike itself. What sets that drain speed is headroom: capacity minus normal traffic.
When the average arrival rate sits above capacity, no queue size saves you; Azure Service Bus will eventually reject sends with QuotaExceeded. Scaling consumers on depth is no free fix either, because instances times per-instance concurrency lands on the database the queue was meant to protect. Measure message age with ServiceBusReceivedMessage.EnqueuedTime, and pair TimeToLive with DeadLetteringOnMessageExpiration so stale work does not slow the drain.
Real-world use
- Sizing the drain time of a Service Bus queue behind an e-commerce API that spikes on sale days
- Pinning scaling limits in an Azure Functions Service Bus-triggered worker to what the database can absorb
- Setting TimeToLive and dead-lettering on short-lived messages such as driver-proximity notifications
- Deciding that a synchronous HTTP endpoint asking for an immediate answer should not sit behind a queue at all
Why it matters
A queue hides the problem instead of fixing it: without a quick calculation of average rate, headroom and downstream limits, you discover months later that the backlog grows every day and the wait has no owner.