Списание баланса пользователя: гонка при конкурентных вызовах
Код-ревьюMiddle15 минпроверка — Спринт14-t030
Дана таблица users(user_id, balance) и псевдокод функции withdrawBalance (два варианта записи одной и той же логики): в транзакции READ COMMITTED она читает баланс, проверяет, что денег хватает, и списывает сумму. Найдите гонку при конкурентных вызовах и всё, что ещё плохо; предложите рефакторинг.
Дополнительно: что будет, если два экземпляра сервиса одновременно получат запрос на списание с одного счёта — как это скажется на консистентности данных?
Как на собесе: 15 минут, ищи всё — гонки, утечки, ошибки, безопасность, стиль. Ссылайся на строки.
review.gogo
1--Юзерыиихбалансы2CREATETABLEusers3(4user_idINTEGERPRIMARYKEY,5balanceREALNOTNULL6);78// Вариант 1 (псевдокод). Снятие денег с баланса пользователя9funcwithdrawBalance(userIdint32,amountfloat32)float32{10exec("BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED")1112oldBalance=exec("SELECT balance FROM users WHERE user_id = $1",userId)13ifoldBalance>=amount{14exec("UPDATE users SET balance = balance - $1 WHERE user_id = $2",amount,userId)15}16newBalance=exec("SELECT balance FROM users WHERE user_id = $1",userId)1718exec("COMMIT")1920returnnewBalance21}2223// Вариант 2 (псевдокод) — то же самое через объект tx24funcwithdrawBalance(userIdint32,amount){25tx:=BeginTransaction("ISOLATION LEVEL READ COMMITTED")2627oldBalance:=tx.exec("SELECT balance FROM users WHERE user_id = $1",userId)28ifoldBalance>=amount{29tx.exec("UPDATE users SET balance = balance - $2 WHERE user_id = $1",userId,amount)30}31newBalance:=tx.exec("SELECT balance FROM users WHERE user_id = $1",userId)3233tx.Commit()3435returnnewBalance36}