race condition при получении бонуса

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

Выглядит код так:

if($user_info['bonus_last_time'] + 1800 > $time) {
 echo 'Бонус можно получать один раз в 30 минут.';
} else {
 $db->query("
  UPDATE `users`
  SET `coins` = `coins` + 1000,
      `bonus_last_time` = '$time'
  WHERE 
      `id` = '".$user_info['id']."';
 ");

 echo 'Бонус успешно получен.';
}

Проблема заключается в том, что если воспроизвести этот запрос одновременно несколько раз, то за раз можно получить бонус несколько раз и несмотря на условие, запросы всё равно проходят, то есть получается race condition.

Понимаю, что нужно использовать транзакции, чтобы избежать такого, но почему-то кажется не совсем правильным решением, а что если одновременно 1000 человек получат бонус, вдруг что-то пойдёт не так с транзакциями? И если честно, то хотелось бы другой способ, но я не знаю какой. Была идея создать отдельную таблицу и назвать её например users_bonus_queue с уникальным полем user_id и делать примерно что-то такое:

if($user_info['bonus_last_time'] + 1800 > $time) {
 echo 'Бонус можно получать один раз в 30 минут.';
} else {
 if($db->query("
  INSERT INTO `users_bonus_queue` (`user_id`)
  VALUES ('".$user_info['id']."')
 ")) {
  $db->query("
   UPDATE `users`
   SET `coins` = `coins` + 1000,
       `bonus_last_time` = '$time'
   WHERE 
       `id` = '".$user_info['id']."';
  ");

  echo 'Бонус успешно получен.';
 } else {
  echo 'Что-то пошло не так.';
 }
}

Но тоже непонятно, в каком случае нужно потом удалить запись с пользователем в таблице users_bonus_queue, чтобы пользователь потом смог снова получить бонус.

Помогите, пожалуйста, как правильно сделать. Я несколько дней искал в поисковиках свою проблему, мучал chatGPT, но по итогу так и не понял если честно как мне быть. Надеюсь на помощь сообщества.


Ответы (0 шт):