Например, нужно сделать трансфер от одного пользователя на другой:
получаем баланс пользователя из базы
проверяем хватает ли средств для перевода по полученному балансу
обновляем значения у одного пользователя и другого в базе
Как здесь более правильно защититься от race condition при переводе денег от одного пользователя другому?
В сущности весь указанный вами алгоритм для операции списания не нужен. Достаточно
begin;
update tablename set balance = balance - :amount
where user_id = :user and balance >= :amount;
update tablename set balance = balance + :amount
where user_id = :user_target;
-- прочие действия, из-за двух update выше мы
-- гарантированно сериализованы для этих двух пользователей
commit;
Необходимо только ловить ситуацию с affected rows = 0. Если первый update ответил, что ничего не обновил - значит или нет пользователя отправителя или у него нет денег. Универсально это не проверить, а говоря о конкретике конкретных диалектов SQL - вполне могут быть способы. Если второй update ничего не изменил - значит не существует получателя. В обоих случаях вам необходимо сделать rollback.
Даже если говорить о полновесной двойной бухгалтерской записи текущий баланс обычно всё равно денормализуют и на его обновлении и можно выстраивать очередь транзакций. Ну а если нет денормализованного баланса - то придётся придумывать другие места для сериализации транзакций. Может быть через advisory locks (если ваша СУБД их предоставляет).
Если же говорить о вашем алгоритме, то для сериализации транзакций необходимо будет при получении текущего баланса взять соответствующую блокировку строки. Для намерения эту строку изменить необходима эксклюзивная блокировка:
begin;
select ... from tablename where ... for update;
-- прочие действия, до завершения этой транзакции
-- эту строку никто другой изменить не сможет
commit;
Тот, кому нужно перевести, в момент успешного выполнения это будет + к текущему балансу, и не важно какой у него он на тот момент, ведь так?
А вот тот, кто переводит, изначально, при расчете может быть другая сумма нежели при выполнения скрипта, здесь уже в момент выполнения нужно проверять и если суммы не хватило выводить сообщение, я думаю ничего сложного в этой ситуации нет.
Однако, как заметил idd в комментах может случиться так, что между проверкой баланса и записью во время выполнения скрипта, может произойти другая запись, в таком случае нужно использовать некий счетчик, который позволит определить время работы транзакции и заблокировать на это время выполнение других транзакций.
Начинаем обмен средствами, даем 2 минуты и записываем в базу время окончания плюс некий скажем хеш транзакции (также помещаем его в куки браузера), при выполнении отсылаем хеш с данными где проверяем и если хеш совпадает и время не вышло, записываем, иначе можно оповестить что время истекло или сессия устарела и т.п. В момент когда мы попытаемся создать 2-ой перевод, проверив что уже существует хеш у этого пользователя (он также не совпадает с хешем в браузере) и время не вышло вернем ошибку о том, что необходимо подождать завершения предыдущей транзакции.
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
не могли бы вы подсказать какие-либо материалы, в которых описано взаимодействие с кошельком Bitcoin Core средствами PHP(Необходимо для организации...
Создать массив $ и записать в него 5 произвольных цифрИспользуя цикл for вывести все цифры умноженные на 5
Подскажите как можно спарсить данные из ical - файл https://wwwairbnb
Пытаюсь добавить в архив файлы из указанной директории с помощью стандартного класса ZipArchive, однако архив не создаетсяЧто я делаю не так?...