Backpressure Lab
A bounded queue you can push past its own capacity, and three different answers to what should happen next.
Every queue between two components has a capacity. The interesting question is never how big it is — it is what happens when it is full, and there are only three answers. Make the producer wait, which is backpressure and moves the cost onto whoever is producing. Refuse the arrival, which keeps a backlog that is already stale. Or discard the oldest waiting item, which keeps the backlog fresh and abandons work that was already accepted. Run the same load through each and they diverge within seconds.
Nothing on this page touches a chain.
Where the work happens
The simulation runs on the server, in Go, and the figures reach this page over a server-sent event stream ten times a second. That is the opposite of the keygrind demo next door, where everything happens in your tab — and it is why this page has limits that one does not need. A session starts real goroutines on a real box and holds them, so the server admits 4 simulations at a time, one per address, and runs no more than 32 workers across all of them.
Every control below has a hard ceiling that is applied in the simulation, not in the browser: at most 8 workers, 5000 messages a second, a service time between 1 and 50 ms, a queue no deeper than 10000, and a session no longer than 90 seconds. Ask for more and the server clamps it and tells you what it changed — the numbers on this page are always the numbers that ran.
Idle. Pick a policy and press Run it.
Queue depth over the session. Flat at the top is a queue that is permanently full.
- Offered
- —
- Completed
- —
- In flight
- 0
- p50 latency
- —
- p95 latency
- —
- p99 latency
- —
Latencies are measured end to end, from the instant a message arrived to the instant a worker finished with it, and are read off a fixed bucket ladder — so each figure is the top of the bucket its percentile falls in and never reads lower than the truth.
What to try
- Leave the defaults and run it three times, changing only the policy. The arrival rate is above what the pool can service, so all three overflow — and only one of them stalls.
- Watch the offered rate under “Block the producer”. It settles below the rate you asked for, because the queue is reaching back and slowing its own source. That gap is the whole idea.
- Raise the queue capacity under a dropping policy. Fewer messages are shed and the tail latency gets worse: a deeper buffer does not add capacity, it converts loss into waiting.
- Drop the arrival rate below the stated capacity. The queue stops mattering, and all three policies look identical — which is why a queue that is never full tells you nothing about the one you chose.