API Design

Production at Scale · Simulator 04

Queue & backpressure

A bounded queue between producers and a worker pool. Drag the producer rate above workers × per-worker throughput and watch the queue fill, consumer lag balloon, and backpressure begin dropping messages. This is the model behind Kafka consumer lag, SQS queue depth, and any async pipeline that can be overwhelmed.

InteractiveAnimatedModels rel-10
💡 How to Read the Simulator

Line = queue depth over time, normalized to queue max. Red = queue at capacity (messages being dropped). Green = draining.

By the numbers: queue math

The queue grows whenever producers outpace consumers, and shrinks whenever consumers catch up. The key relationships:

# Consumer throughput (total msgs/s the worker pool can handle)
throughput   = workers × perWorker

# Net fill rate: positive means queue is growing
net          = producer − throughput

# Queue depth evolves each tick (clamped to [0, qmax])
depth(t+dt)  = clamp(depth(t) + net × dt, 0, qmax)

# Consumer lag: how far behind in time the consumers are
lag          = depth / max(1, throughput)   # seconds

# You need this many workers to keep up
min_workers  = ceil(producer / perWorker)

The fundamental rule: you need workers ≥ producer / perWorker to prevent unbounded queue growth. Below that threshold, the queue fills to capacity and backpressure (dropping or blocking) becomes unavoidable. Consumer lag is the leading indicator — it rises before you start dropping.

Worker Scale Sensitivity Table

Producers Per-Worker Rate Workers Needed Under-provisioned Lag Growth Rate Queue Operation Sequence
10,000/s 1,000/s 10 +1,000 msgs/s (with 9 workers) ENQUEUE → Queue Full → DROP / BLOCK
50,000/s 2,000/s 25 +10,000 msgs/s (with 20 workers) ENQUEUE → Queue Full → DROP / BLOCK
100,000/s 5,000/s 20 +25,000 msgs/s (with 15 workers) ENQUEUE → Queue Full → DROP / BLOCK
✅ Try this

1. Set producer to 10 k/s, 10 workers at 2 k/s each → throughput = 20 k/s → queue drains. 2. Raise producer to 30 k/s → net = +10 k/s → watch the queue fill and lag climb. 3. Add workers until throughput exceeds producer again — the queue drains. 4. Set qmax very small (e.g. 1 k) with a fast producer → backpressure starts almost immediately.

⚠️ Modeled, not measured

This is a first-principles model of a bounded queue, not a capture of any real message broker's behaviour. Real systems (Kafka, SQS, RabbitMQ) have rebalancing, partition assignment, fetch batching, and flow-control mechanisms that affect observed lag. The model shows the structural dynamics — treat numbers as illustrative.

Under the hood: how queues & backpressure work

A message queue acts as a structural shock absorber. Internally, brokers write incoming payloads to disk-backed ring buffers (such as commit logs in Kafka) or memory queues:

  1. Producer Enqueue: A client app sends a batch write request. If the broker's buffer is within limits, it appends the message and returns HTTP 202.
  2. Consumer Pull: Worker instances poll the queue in a loop:
    while (true) {
      const batch = queue.poll(size: 100, timeout: 50);
      process(batch);
      queue.commit();
    }
  3. Backpressure Enforcement: When consumer processing lag exceeds limits or when the queue hits capacity, the broker stops accepting new writes, returning status codes like RESOURCE_EXHAUSTED or blocking TCP socket reads.

How to debug & inspect it

Use command-line CLI tools provided by message brokers to check active consumer positions and calculate total consumer lag.

$ kafka-consumer-groups.sh --bootstrap-server localhost:9092 \ --describe --group order-processors GROUP TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG order-processors orders-topic 0 4820194 4830194 10000 order-processors orders-topic 1 4820010 4820012 2 # Lag of 10,000 on partition 0 indicates a hot partition bottleneck

Symptom → Cause → Fix:

Symptom Likely Cause Fix
Lag climbs steadily during traffic spikes Consuming workers are under-provisioned or processing is too slow Autoscale the worker fleet horizontally or optimize consumer thread performance
Unbalanced lag (one partition has massive lag, others have zero) Skewed partition key hashing sending most requests to one worker Modify routing key generation to spread traffic evenly across partitions
Workers repeatedly crash and rebalance, losing throughput Processing a batch takes longer than the broker's maximum poll interval Decrease the fetch batch size or increase the maximum processing timeout setting

Math companion — this page trains intuition with numbers. Full design contracts live in rel-10-event-driven-pubsub.html.

When not to stop at the simulator

Why the numbers matter

If you cannot re-derive the formulas on this page from first principles (arrivals, service time, capacity, amplification), you do not own the control. Recompute by hand once, then change one input and predict the chart.

Failure translation

Map each sim control to an ops action: raise limit → client 429; add cache hit ratio → DB QPS falls; add jitter → retry peak flattens; add workers → lag falls until lock or DB saturates.

Sources & further reading