Queue Fairly or Fold: Backpressure Lessons from a UK Ticket Drop
Photo: Vision360media, CC BY-SA 4.0, via Wikimedia Commons
At 8:59 on a sale morning, most ticket-site loaders are lying. The page says you are in a queue. The loader is actually accepting every request, opening every connection and hoping the backend will somehow absorb a crowd that arrived all at once. Then 9am lands, the spinner becomes a shrug, and the site starts doing the technical equivalent of shouting into a crowded room. The problem is not traffic. The problem is that the loader was designed to be optimistic when it needed to be honest.
A queue is a product, not a spinner
If you are selling tickets for a UK festival, the waiting room is part of the experience. It needs to tell people where they are, roughly how long it might take, and what happens if they refresh. A loader that hides all of that behind a spinning circle is not protecting the backend. It is just moving the panic to the user.
The first design decision is admission control. Decide how many active sessions your system can genuinely serve, then let the rest wait. Not in memory on one box. Not in a Redis list with no expiry. In a queue with a clear contract: you are admitted, you are waiting, or you are out. Everything else is decoration.
Rate limits are not rude
A ticket drop attracts scripts, refreshes and genuinely excited humans. A token bucket per IP address is table stakes, but it is not enough on its own. You also need per-session limits and a global ceiling that the loader respects even when the edge is healthy. If the checkout service can handle 400 reservations a minute, the loader should not hand it 4,000 and hope for the best.
This is where backpressure earns its keep. When the downstream service starts returning 429s or slowing down, the loader should slow down too. That sounds obvious. In practice, a lot of loaders treat a 429 as an invitation to retry harder. That is not resilience; it is a distributed denial-of-service attack with good intentions.
Fairness beats raw throughput
A festival ticket sale is not a benchmark. You are not trying to maximise requests per second. You are trying to give a large number of people a believable chance without letting one person with six browser tabs and a script take the whole queue.
A practical pattern is a shuffled waiting room. Everyone who arrives before the sale opens gets a random position, not a first-come-first-served position. That removes the incentive to hammer refresh at 8:59:59. After the sale opens, new arrivals go to the back. You can batch admissions every few seconds, hand out short-lived tokens, and expire sessions that go quiet. The queue becomes a controlled release valve rather than a stampede.
The loader should also be honest about capacity. If there are 50,000 people waiting and 2,000 tickets left, saying you are in a queue is technically true but emotionally useless. A better waiting room says: demand is very high, not everyone will get a ticket, please do not refresh. That is not defeatism. It is respect for the user and protection for the system.
Degrade the right things
Graceful degradation is not a single switch that turns the site into a static page. It is a set of decisions made in advance. Disable the recommendation carousel. Drop the live seat map. Stop personalising the header. Keep the queue, the ticket count and the checkout path alive. Those are the parts that matter.
The loader should know which dependencies are optional. If the stock-count service is slow, show a cached estimate with a timestamp. If the payment provider starts timing out, stop admitting new sessions until the queue drains a little. If the queue store itself fails, fail closed with a clear message rather than letting everyone through to a broken checkout.
This is where UK infrastructure reality bites. You may have traffic from London, Manchester, Glasgow and a hundred smaller towns, all arriving on different connections. You cannot assume low latency or a single time zone. You can assume that people will refresh. Design for that.
Test the ugly version
A load test that only checks the happy path is a marketing exercise. You need a test where the queue store is slow, the checkout service is rate-limiting, and the edge is still accepting connections. Then you watch what the loader does. Does it retry forever? Does it admit everyone? Does it show a spinner that means nothing?
The best ticket loaders are the ones that behave like a good bouncer: calm, firm and clear about the capacity of the room. They do not promise everyone a ticket. They do not pretend the queue is shorter than it is. They shed load honestly, in a way that keeps the site alive long enough for the people who do get in to actually buy something.