Регистрация бронирования в отеле с проверкой наличия свободных номеров

Проверку доступности, уменьшение остатков и создание заказа выполняйте в одной транзакции с блокировкой всех ночей проживания.

Короткий ответ

Что ответить на собеседовании

Проверку доступности, уменьшение остатков и создание заказа выполняйте в одной транзакции с блокировкой всех ночей проживания.

Подробный разбор

Ответ с пояснениями

Условие

Реализуйте функцию create_order(user_id: int, hotel_slug: str, start_date: date, end_date: date), которая сохраняет новое бронирование в базе данных. Если на выбранные даты нет свободных номеров, функция должна вернуть соответствующее сообщение.

Таблица availability (доступность номеров):

hotel_slug date rooms_count
ibis_batumi 2023-12-01 2
ibis_batumi 2023-12-02 1
ibis_batumi 2023-12-03 2

Таблица orders (существующие бронирования):

user_id hotel_slug start_date end_date
1 ibis_batumi 2023-12-01 2023-12-03

Сценарий работы функции:

  • Клиент 1 (оформил бронь чуть раньше):

    • заказ успешно создаётся;
    • количество свободных номеров уменьшается на 1 для каждой даты периода.
  • Клиент 2 (пытается забронировать те же даты):

    • получает сообщение, что свободных номеров нет.
def create_order(user_id: int, hotel_slug: str, start_date: date, end_date: date):
    pass

Ответ

Проверку доступности, уменьшение остатков и создание заказа выполняйте в одной транзакции с блокировкой всех ночей проживания.

Считаем rooms_count текущим остатком, а период проживания — [start_date, end_date): день выезда не занимает номер. Для Django и моделей Availability, Order схема такая:

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

@transaction.atomic
def create_order(user_id, hotel_slug, start_date, end_date):
    nights = (end_date - start_date).days
    if nights <= 0:
        raise ValueError('Некорректные даты')
    rows = list(Availability.objects.select_for_update().filter(
        hotel_slug=hotel_slug, date__gte=start_date,
        date__lt=end_date).order_by('date'))
    if len(rows) != nights or any(r.rooms_count < 1 for r in rows):
        return 'Нет свободных номеров'
    Availability.objects.filter(pk__in=[r.pk for r in rows]).update(
        rooms_count=F('rooms_count') - 1)
    return Order.objects.create(user_id=user_id, hotel_slug=hotel_slug,
                               start_date=start_date, end_date=end_date)

Нужны уникальность (hotel_slug, date) и запрет отрицательных остатков. Второй конкурентный запрос дождётся блокировок и увидит обновлённые значения. Уже учтённые заказы повторно из остатков не вычитают.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее