Booking a room at the same time

Booking a room at the same time

Making a reservation.

·

3 min read

How would you handle different users booking a room at the same time?

In most cases, as a developer, you are faced with the challenge of preventing multiple users from booking the same room or a reservation at the same time. We will try and resolve this issue with example code snippets using Django.

We will use two strategies for this and I will show you each with some sample code.

  1. Pessimistic locking: This strategy involves locking the record for a room when a user starts the booking process and releasing the lock only when the booking is complete. This prevents other users from trying to book the same room until the lock is released.

  2. Optimistic locking: This strategy involves adding a version number to the record for a room, and checking the version number when a user attempts to book the room. If the version number has changed since the user started the booking process, it indicates that another user has made a change to the record, and the booking process is aborted.

Here is some sample code that shows how you might handle concurrent bookings for a hotel room in Django.

Pessimistic locking:

from django.db import transaction

from .models import Room, Reservation


@transaction.atomic
def book_room(room: Room, checkin_date, checkout_date, guest_name, guest_email):
    """
    Book a room for the specified checkin and checkout dates, for the specified guest.
    """
    # Check if the room is available for the specified dates
    if not room.is_available(checkin_date, checkout_date):
        raise ValueError("Room is not available for the specified dates")

    # Create a new reservation for the room and guest
    reservation = Reservation.objects.create(
        room=room,
        checkin_date=checkin_date,
        checkout_date=checkout_date,
        guest_name=guest_name,
        guest_email=guest_email,
    )

    # Mark the room as booked for the specified dates
    room.book(checkin_date, checkout_date)

    return reservation

In the above code, we used the transaction.atomic context manager to ensure that the database updates are performed atomically, so that two users cannot book the same room at the same time. We first check if the room is available for the specified dates using the is_available method of the Room model. If the room is available, we create a new Reservation object with the specified details, and mark the room as booked for the specified dates using the book method of the Room model. If the room is not available, we raise a ValueError to indicate that the booking cannot be completed.

Here's an example of optimistic locking:

from django.db import models
from django.db.models import F

class HotelRoom(models.Model):
    room_number = models.CharField(max_length=50)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    available = models.BooleanField(default=True)
    version = models.IntegerField(default=1)

    def save(self, *args, **kwargs):
        # Retrieve the latest version of the room from the database
        latest_room = HotelRoom.objects.get(id=self.id)

        # Check if the version of the room in the database matches the version of the room being saved
        if latest_room.version != self.version:
            raise ValueError("Room has been modified by another user")

        # Increment the version number of the room being saved
        self.version += 1

        # Update the available field to reflect the number of rooms left
        if self.available and latest_room.available:
            self.available = (HotelRoom.objects.filter(id=self.id, available=True)
                                .update(available=False) > 0)

        super(HotelRoom, self).save(*args, **kwargs)

There are still some other strategies that you can still use to prevent this from happening, and they are as follows:

  1. Queueing: This strategy involves placing users in a queue when they attempt to book a room, and processing the bookings in the order they were received. This can help to prevent conflicts but may result in some users experiencing longer wait times.

  2. Timeouts: This strategy involves setting a timeout for the booking process, and releasing any locks or other resources if the timeout is exceeded. This can help to prevent resources from being tied up indefinitely, but may also result in some users having their bookings canceled if they take too long to complete.

See you at the next one.