Urgent.News

the world's headlines, one feed

Editions

Tech

What Happens When 20,000 People Click the Same Seat

Building a seat picker is easy. You render a map, you colour the free seats blue, you let people click. Then tickets go on sale for a big fixture, and thousands of people click the same seat inside the same second. I spent several years building booking systems for large events — one of them handling twelve to twenty thousand spectators per match. Almost none of the difficulty was in the seat…

Booking systems for large events face three main challenges when multiple users attempt to select the same seat simultaneously. The first issue is stale availability, where the seat map already reflects incorrect information. The second problem is a lost update, which occurs when two users pass a "is this seat free?" check milliseconds apart and both proceed to claim the seat, resulting in one user finding out at the turnstile.

The third challenge is the half-granted basket, where a family requests four seats together, but two are granted while the other two are grabbed mid-request by someone else, leaving nobody with a usable basket and two stranded seats.

To tackle these issues, the design decision is made that clicking a seat does not sell it. Instead, clicking creates a hold – a short-lived, expiring claim. The actual purchase is performed during checkout. This approach involves two stores: one in Redis for holds and another in Postgres for orders. Holds live in Redis because they are high-churn, short-lived, and mostly abandoned. They are modelled as Redis keys that automatically expire, eliminating the need for reapers or cleanup jobs.

However, Redis is merely an optimisation and not the guarantee of non-duplicate seat sales. The actual guarantee lives in the Postgres database through a unique constraint on the order_seats table. The checkout path consists of two gates: the first gate verifies that the hold has not expired using Redis, and the second gate performs the actual database insertion.

If a race condition occurs and a seat is sold between the hold verification and the database insertion, the second gate will catch the error (23505) and translate it into a 409 Conflict exception.

The hold verification is done using a Lua script in Redis, ensuring that the check and set operations happen atomically. This prevents the half-granted basket issue, as either the entire basket is granted or none of the seats are granted. If a hold expires or is invalidated, the release process compares and deletes the token to ensure the seat becomes available again. By separating the hold verification and the actual database insertion, the system can handle concurrency effectively and provide a better user experience.

Written by urgent.news from Dev.to's reporting — not their text. Machine-written — it may contain errors, so check the original before relying on it.

Read the original at dev.to →

More in Tech

[Sponsor] Drata

Leverage autonomous AI agents to automate compliance, manage internal and third-party risk, and continuously prove your security posture. ★

Text at 1:1 contrast is not an axe violation. It is incomplete.

I shipped a button whose label was invisible. Same colour as its own background — 1:1 contrast, no readable text at all. It was only broken in dark mode, and my CI was green the entire time.

  • Text with 1:1 contrast is invisible in both light and dark modes
  • Axe accessibility tool does not report invisible text as violation
  • Reporter created a11y-matrix tool to report accessibility issues in different modes