Добавление резервирования без временных конфликтов

#symfony #transactions #doctrine #mariadb #locking

#symfony #транзакции #доктрина #mariadb #блокировка

Вопрос:

Я разрабатываю систему бронирования (с использованием PHP, MariaDB, Symfony и Doctrine), в которой каждый пользователь может забронировать номер в любое желаемое время, но не может добавить бронирование, которое перекрывает другое в том же номере. Поэтому перед каждой вставкой я выполняю ВЫБОР, чтобы проверить, доступно ли время резервирования.

 // ReservationRepository.php
public function getConflictIds(Reservation $rsvn, int $limit = 1): array
{
    return $this->createQueryBuilder('rsvn')
        ->select('rsvn.id')
        ->where('rsvn.room = :roomId')
        ->andWhere(':beginTime < rsvn.end_time')
        ->andWhere(':endTime > rsvn.begin_time')
        ->setMaxResults($limit)
        ->setParameters([
            'roomId' => $rsvn->getRoom()->getId(),
            'beginTime' => $rsvn->getBeginTime(),
            'endTime' => $rsvn->getEndTime(),
        ])
        ->getQuery()->getResult();
}
  
 // ReservationController.php
$em = $this->getDoctrine()->getManager();
$repo = $this->getDoctrine()->getRepository(Reservation::class);
$em->beginTransaction();
try {
    if (count($repo->getConflictIds($rsvn)) > 0) {
        throw new Exception();
    }
    $em->persist($rsvn);
    $em->flush();
    $em->commit();
    // redirect
} catch (Exception $e) {
    $em->rollback();
}
  

Я боюсь, что первый пользователь проверит, доступно ли резервирование (ВЫБЕРИТЕ), в то время как второй пользователь также проверит это до того, как первый пользователь выполнит ВСТАВКУ, поэтому оба они добавят заказы, которые могут перекрываться.

Я думаю, было бы лучше запретить другим пользователям проверять доступность (ВЫБРАТЬ) до того, как первая транзакция завершит транзакцию, но я открыт для других решений. Я рассматриваю четыре варианта, но я не знаю, какой из них будет лучшим в этом случае:

  1. БЛОКИРОВКА записи резервирования ТАБЛИЦЫ;
  2. ВЫБЕРИТЕ * ИЗ номеров, ГДЕ id = ? ДЛЯ ОБНОВЛЕНИЯ;
  3. УСТАНОВИТЕ СЕРИАЛИЗУЕМЫЙ УРОВЕНЬ ИЗОЛЯЦИИ ТРАНЗАКЦИЙ;
  4. Выполните SELECT после ВСТАВКИ и выполните ОТКАТ, если, кроме добавленной строки, есть еще одна, которая вызывает конфликт.

Любые предложения, спасибо