Спасите авиакомпанию от банкротства — решите задачу для разработчиков

Знакомы с Python и PostgreSQL? Проверьте, удастся ли вам устранить путаницу с билетами.

Спасите авиакомпанию от банкротства — решите задачу для разработчиков

Условие

На сайте авиакомпании произошел сбой. Два человека одновременно забронировали последнее оставшееся место в самолете — и оба оплатили билет. Но затем один из пассажиров получил ошибку о том, что его место уже занято. В итоге авиакомпания понесла репутационные и финансовые потери.

Отдел серверной разработки передал вам подробности об устройстве базы данных. Что известно?

В рамках системы бронирования авиабилетов есть база данных с двумя сущностями:

Users:

  • id — уникальный идентификатор пользователя.

Tickets:

  • id — уникальный идентификатор билета;
  • from — место вылета;
  • to — место назначения;
  • place — номер сидения в самолете;
  • flight_at — дата и время рейса;
  • reserved_at — время последнего бронирования;
  • reserved_by — идентификатор пользователя, который последним забронировал билет;
  • owner — идентификатор пользователя, купившего билет (может быть NULL, если билет только забронирован).

В таблице Tickets изначально вбиты все билеты на конкретный рейс, то есть новые записи в базе данных не появляются.

Также у вас есть участок кода с бэкенд-сервиса, отвечающего за бронирование. Он написан на Python:

place = "4A" flight_id = "AS579" user_id = 123 ticket_query = "SELECT * FROM tickets WHERE place=%place AND flight_id=%flight_id" ticket = db.execute(ticket_query, (place, flight_id)) if ticket.reserved_at < now() - 10 * MINUTE: reserve_ticket_query = "UPDATE tickets SET reserved_by=%user_id WHERE id=%ticket_id" db.execute(reserve_ticket_query, (user_id, ticket)) return "Можете оплатить билет по ссылке: https://..." else: return "Билет забронирован другим пассажиром"

Задача

Предложите решение, которое запретит нескольким пользователям одновременно бронировать одно место.

Подсказка: решение может быть основано на работе с базой данных, а именно на изменении SQL-запроса, который отправляется в PostgreSQL.

Знаете ответ? Делитесь своими вариантами в комментариях! А если нужна помощь, смотрите решение в Академии Selectel.

6
3
11 комментариев

Интересно, а в реальных компаниях такой же код? Или это просто для этой конкретной задачи?

1

А вот этого мы никогда не узнаем)

1

Нужно использовать транзакции в PostgreSQL с уровнем изоляции SERIALIZABLE или REPEATABLE READ

В данном случае рекомендуется использовать FOR UPDATE для блокировки записей при выполнении SELECT

`SELECT * FROM tickets WHERE place=%place AND flight_id=%flight_id FOR UPDATE`
`UPDATE tickets SET reserved_by=%user_id WHERE id=%ticket_id`

1

Такие штуки надо осуществлять транзакциями — операциями, которые могут выполниться успешно целиком или не выполниться вовсе. Должно быть что‑то вроде:

try:
# Начинаем транзакцию. Важно, чтобы база данных поддерживала транзакции.
db.begin_transaction()

# Запрашиваем базу данных. Что‑то вроде…
ticket = db.execute(SQL-запрос здесь).fetchone()

if ticket:
db.rollback_transaction()
return "Билет не найден"

Всё нормально, осуществляем запись…

except Exception as e:
db.rollback_transaction()
finally
db.end_transaction()

транзакция без изоляции не поможет. Здесь важна блокировка таблицы во время чтения.

Транзакция просто отменить обновление если будет ошибка во время запроса

1

недавно узнала, что абсолютно все авиакомпании продают намного больше билетов, чем есть на самом деле. непонятно только зачем??