Booking System with Pessimistic Locks

Alexander Kozhenkov
Javarevisited
Published in
4 min readOct 3, 2021
Photo by JESHOOTS.COM on Unsplash

There is a common task of creating a booking system: buying tickets at the cinema, choosing seats on the plane, etc. This article will look at how to build such a system based on RDBMS or key-value storage. I will use PostgreSQL as an example of relational databases and Apache Ignite as key-value storage.

It is important to note that we will consider the case where overbooking is not allowed. As a rule, plane tickets are sold, allowing for overbooking (they can sell more tickets than seats on the plane) because passengers often cancel tickets. But when we book particular seats, we expect that we will be the only holders.

Pessimistic or optimistic locks

First of all, let’s figure out which of the lock mechanisms we should use.
Most tasks assume that we have such a flow:

  1. A user sees the scheme of the cinema hall with available and reserved seats.
  2. The user selects the desired places and sends a request to the server.
  3. Places are reserved for 5–15 minutes, during which the user must make payment.
  4. If the payment was successful, the seats are considered purchased.

As soon as we described the flow, it became clearer how to act. We cannot use optimistic locking when waiting for payment since this will mean that several users may have time to pay for seats, but only the first of them will get it. But for the initial reservation, we can use any of the locks.

Booking one seat

First, let’s look at the case of booking only one seat because it is a bit simpler. The whole point is to synchronize the state with a specific object field (or a table column). Let’s define lock_until for this purpose.

The part of the database structure will look like this:

In that article, I showed how you could update the entity using select-for-update (pessimistic locks), so let’s now use versioning (optimistic lock).

The update statement is presented below:

And the actual business logic is here:

Reserving with PostgreSQL

In the case of key-value storage, the only difference is to use replace instead of update.

Reserving with Apache Ignite

When the payment is made, we reset lock_until explicitly and update the status.

Booking many seats

Often we have to create a service for booking not one, but several seats. So we need to decide how to deal with conflicts if concurrent requests overlap a few seats.

Both users are unsatisfied

If we do nothing, there may be a case when the first request reserved 2 out of 3 seats successfully, as well as the second request. As a result, both users are unsatisfied. Unlikely, they will pay for only two seats.

It is much preferable for us if one of the requests will complete failure and the second one will succeed.

Only one user is unsatisfied

To do so, we can use table locks for relational databases, but it is not an option for key-value storages. Also, table locks will slow down overall performance for all other cinema sessions.

Instead, we can use the lock_until approach for cinema_sessions table too. We need to take this lock for only 10–15 seconds maximum for reserve operation processing.

Locking CinemaSession with Apache Ignite

Now only one user can reserve a group of seats at the same time. And once it’s done, we immediately release the lock for the particular cinema session. Also, different cinema sessions don’t impact each other.

Other Java Concurrency and Multithreading Articles and Resources You may like

--

--