Предотвращение двойного исполнения.PHP финансовое приложение - PullRequest
0 голосов
/ 08 февраля 2019

Если у меня есть сценарий, который уменьшает баланс пользователей в системе двойного учета, и злонамеренный пользователь решает выполнить этот сценарий на своем аккаунте на двух разных компьютерах (или на одном и том же компьютере) ровно в одно и то же время,Все это будет работать дважды, верно?ТАК, вот мой упрощенный гипотетический сценарий.

    $balance = $user->ledger->getBalance(); // returns 5000
    $amount = 3000;
    if ($amount <= $balance) {
        $user->ledger->decrease($amount);
    }

    echo $user->ledger->getBalance(); // echo's 2000

Если скрипт запускается один за другим, второе выполнение завершится неудачно, потому что в учетной записи осталось только 2000, и он попытался уменьшить на 3000.

Если сценарии выполняются одновременно в одно и то же время, не будут ли оба баланса равны 5000, а оба выполнения сценария вычитают 3000, оставляя отрицательное значение в бухгалтерской книге?

Как можно предотвратитьтакое происходит?Чрезвычайно важно поддерживать целостность данных в этой таблице базы данных.

1 Ответ

0 голосов
/ 08 февраля 2019

Вы говорите о условиях гонки , и они чрезвычайно важны для устранения в финансовом коде.

Все, что следует шаблону получения / тестирования / установкибудет иметь огромные проблемы.Вы не можете сделать это.

Вместо этого используйте шаблон set / test / fail.Попробуйте и вычтите использование атомарного оператора SQL, такого как отдельная операция или блок транзакции.Если это приведет к отрицательному сальдо баланса, откатите его назад.

Например, это плохо :

balance = query("SELECT balance FROM accounts WHERE account_id=?")
balance -= amount
balance = query("UPDATE accounts SET balance=?")

Что-то может произойти между извлечением и записью.

Вместо этого вы можете сделать это, если этот запрос либо успешен, либо не выполнен, его нельзя прервать:

query("UPDATE accounts SET balance=balance-? WHERE account_id=? AND balance>?")

Этот запрос не будет выполнен, если не будет достаточно остатка.В результате вы получите нулевые строки, измененные.

Вы также можете сделать это с помощью бухгалтерии в стиле двойной бухгалтерской книги, попытавшись вставить необходимые строки бухгалтерской транзакции, а затем проверить SUM(), чтобы убедиться, чтонулевой или положительный результат баланса для исходного счета.Если этого не произойдет, отмените транзакцию с ROLLBACK.Изменения не применяются.

Существует множество способов структурировать эти операторы INSERT, чтобы исключить отрицательное сальдо, например INSERT INTO x SELECT ... FROM y, где вы можете применить условия к подзапросу для возврата нулевых строк.в случае недостаточного баланса.

...