Как избежать race condition при переводе денег?

Например, нужно сделать трансфер от одного пользователя на другой:

1. получаем баланс пользователя из базы

2. проверяем хватает ли средств для перевода по полученному балансу в пункте 1

3. обновляем значения у одного пользователя и другого в базе

Как здесь более правильно защититься от race condition при переводе денег от одного пользователя другому?
  • Вопрос задан
  • 1030 просмотров
Пригласить эксперта
Ответы на вопрос 5
@Yan-s
SQL транзакции
Ответ написан
@frosterdam
Есть ещё вариант, помимо предложенных ранее. Немного экзотический. Но имеет место быть, т.к. сам лично столкнулся с ним, используя вендорское ПО.

Это состояние трансфера в каждый момент времени. Грубо говоря, когда создаётся трансфер, то сразу генерируется запись (в отдельной табличке, например) со статусом это трансфера, например, CREATED. Когда выполняется какое-либо действие, то состояние изменяется (изменяется именно поле статус в записи), например, на LOCKED. И если оно LOCKED, то никакие действия с этим трансфером сделать нельзя. Если же трансфер выполнен успешно, то статус переходит в, например, APPROVED или ACCEPTED. А если в ходе трансфера были ошибки, то он переходит в статус, например, DECLINED.

Как показала практика, то такого решения хватает в 99% случаях.
Ответ написан
Комментировать
Комментировать
Adamos
@Adamos
1. получаем баланс пользователя из базы
2. проверяем хватает ли средств для перевода по полученному балансу в пункте 1
3. обновляем значения у одного пользователя и другого в базе

Вообще-то эти действия при желании выполняются одним атомарным запросом UPDATE ... JOIN ... WHERE.
После которого можно проверить, сколько строк изменил запрос - и либо сохранить информацию об успешно совершенной транзакции (у вас же не только сумма на счетах меняется, правда?), либо вернуть ответ о невозможности перевода.
Ответ написан
Комментировать
Stafox
@Stafox
Web developer со всеми вытекающими...
Вообще, по-хорошему, не должно у Вас быть такого понятия как баланс. Точнее он может быть как кеш результата (сумма `amount` всех транзакций). А при проведении очередной транзакции пересчитывать кеш.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы