← All work
OmnibusLive demoSeat booking · team project at CTU FEL

Two passengers, one seat, no double booking

A bus booking system with a live seat map. When two people grab the same seat at the same moment, only one of them gets it.

Team semester project at CTU FEL by Mykhailo Plokhin and Ivan Shestachenko. A demo, not a real carrier.

Open the live appSource codeomnibus.79.72.58.230.nip.io

Java · Spring Boot · PostgreSQL · React · Docker · k3s

Omnibus home page on a phone: route search and popular routes from the Czech Republic
Omnibus on a phone: journey details for route 501 from Prague to Brno

The problem

Booking systems break exactly when two people want the same thing at the same time — which is also when the business earns money.

  • If the app reads a seat and then writes it, two requests can both see it free and both book it.
  • Overbooking is often repaired afterwards with a nightly job and an apology, instead of being impossible in the first place.
  • Passengers and dispatchers need different rights, not the same endpoint behind a different screen.

How it is built

One service owns reservations and nothing else writes a seat. A contested seat goes through one short transaction that holds a row lock.

Omnibus seat map on a phone: free, taken and panoramic seats row by row
ABwaitingtaken

Passenger A books seat 12. The database locks that row.

Passenger B asks for seat 12 at the same moment and waits for the lock.

A's booking commits. B is told the seat is taken, not double-booked.

tokenreserveFOR UPDATEIngressSpring REST APIRoles · JWTBooking serviceRoutes & fleetPostgreSQL · row locks

Key decisions

Each choice, and the alternative it beat.

Lock the contested seat row

The second request waits instead of overwriting, and the wait is short because the hold is the only work in the transaction. Optimistic retries were the alternative; on a full route they turn into a storm of retries.

One write path for reservations

The timetable, the seat map and the dispatcher only read. With a single writer, "one seat, one passenger" is a rule the system can actually check.

Roles checked at the endpoint

Spring Security resolves the role before the handler runs, so a dispatcher action cannot be reached from a passenger URL.

Numbers

Omnibus is a university project with a live demo, not a commercial service. Measured figures are checked in the code; a target is what it is designed for.

FigureWhat it means
ZeroDouble bookingsThe design target: the database refuses the second writer instead of a cleanup job fixing it later.Target
FOR UPDATESeat lockA pessimistic row lock on the seat query (BookedSegmentRepository).Measured
k3sRuns onContainers on a k3s cluster — the address linked from this page.Measured
Target
— What the build is designed for — not a measured result
Measured
— Checked in the code or on the live build

Need something similar?

Tell us what you need in a few lines. A person from the team replies.

Start a project