Выбор ответа Senior Eloquent, N+1 и миграции обновлено
Гонка при списании баланса
Вопрос
Два запроса одновременно списывают деньги с одного счёта: читают баланс, проверяют, уменьшают и сохраняют. Как исправить в Laravel?
Попробуйте сами
Выберите вариант, и откроется разбор.
Показать ответ и разбор
Коротко
Правильный ответ: В транзакции lockForUpdate() или атомарный условный UPDATE ... WHERE. Без блокировки оба запроса читают одинаковый баланс, оба проходят проверку, и списание происходит дважды (lost update).
Варианты ответа
- В транзакции
lockForUpdate()или атомарный условныйUPDATE ... WHEREправильный ответ - Обернуть код в
try/catchи повторять при исключении - Добавить
sleep(1)перед сохранением, чтобы запросы разошлись - Хранить баланс в сессии пользователя и сохранять в конце
Разбор
Без блокировки оба запроса читают одинаковый баланс, оба проходят проверку, и списание происходит дважды (lost update). SELECT ... FOR UPDATE внутри транзакции заставит второй запрос ждать, а атомарный условный UPDATE решает задачу одним запросом без явной блокировки. Альтернатива: оптимистичная блокировка через колонку версии. try/catch, паузы и сессия гонку не устраняют.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.