Google App Engine (PHP) - Semaphores or alternatives to avoid Race Condition for an Internet Banking

9 views
Skip to first unread message

Mark Millan via StackOverflow

unread,
May 17, 2017, 1:33:08 AM5/17/17
to google-appengin...@googlegroups.com

I am developing an Internet Banking, running on Google App Engine and developed in PHP.

Race condition is a big concern. Users can't ever have less than $0.00 (I know that I have other problems to worry about, but let's focus just on this one right now).

Pseudo-code:

<?php

$user_id =         $_GET['user_id'];
$withdraw_amount = $_GET['withdraw_amount'];

if(getUserBalance($user_id) - $withdraw_amount >= 0){
    setUserBalance($user_id, getUserBalance($user_id) - $withdraw_amount);
    sendMoneyToUser($user_id, $withdraw_amount);
    echo 'Success';
}
else{
    echo 'Insufficient funds';
}

?>

Google Cloud SQL Database:

USER_ID     BALANCE
   1         10.00
   2         20.00

*The above database is a very simplistic version of the actual database. SQL lock tables/rows would not work for me in the real world.

Race Condition case:

GAE instance #1              GAE instance #2

user_id = 1                  user_id = 1
balance = 10                 balance = 10
withdraw_amount = 10         withdraw_amount = 10

10 - 10 >= 0    (true)       10 - 10 >= 0    (true)

setUserBalance(1, 0);        setUserBalance(1, 0);
sendMoneyToUser(1, 10)       sendMoneyToUser(1, 10)

User gets $10                User gets $10

The user now has $20 in his hands, but he had just $10 in the bank!!!

How can I prevent race conditions?

Remember, the code is running on Google App Engine and I don't have access to PHP Semaphores extension.



Please DO NOT REPLY directly to this email but go to StackOverflow:
http://stackoverflow.com/questions/44016197/google-app-engine-php-semaphores-or-alternatives-to-avoid-race-condition-for

Mark Millan via StackOverflow

unread,
May 17, 2017, 1:43:04 AM5/17/17
to google-appengin...@googlegroups.com

I am developing an Internet Banking, running on Google App Engine and developed in PHP.

Race condition is a big concern. Users can't ever have less than $0.00 (I know that I have other problems to worry about, but let's focus just on this one right now).

Pseudo-code:

<?php

$user_id =         $_GET['user_id'];
$withdraw_amount = $_GET['withdraw_amount'];

if(getUserBalance($user_id) - $withdraw_amount >= 0){
    setUserBalance($user_id, getUserBalance($user_id) - $withdraw_amount);
    sendMoneyToUser($user_id, $withdraw_amount);
    echo 'Success';
}
else{
    echo 'Insufficient funds';
}

?>

Google Cloud SQL Database:

USER_ID     BALANCE
   1         10.00
   2         20.00

*The above database is a very simplistic version of the actual database. SQL lock tables/rows would not work for me in the real world. But if you have another solution using SQL instead of PHP, I want to know about it.

Race Condition case:

GAE instance #1                 GAE instance #2    (SAME TIME)

user_id = '1'                   user_id = '1'
withdraw_amount = 10.00         withdraw_amount = 10.00

balance = getUserBalance('1')   balance = getUserBalance('1')
//balance = 10.00               //balance = 10.00

10.00 - 10.00 >= 0  ->  (true)  10.00 - 10.00 >= 0  ->  (true)

setUserBalance ('1',  0.00)     setUserBalance ('1',  0.00)
sendMoneyToUser('1', 10.00)     sendMoneyToUser('1', 10.00)

User gets $10.00                User gets $10.00

The user now has $20.00 in his hands, but he had just $10.00 in the bank!!!

Twisted1919 via StackOverflow

unread,
May 17, 2017, 2:28:06 AM5/17/17
to google-appengin...@googlegroups.com

One way would be to use a mutex library that is using something like redis as a backend so that you can call it from different machines without issues.

Acquire a lock specific to the user before the operation starts, do your work and then release the lock. While you are doing the work, successive calls to do the operation will fail since they cannot acquire the lock.

In pseudocode this would be as simple as:

$lockName = "some string that contains the user/customer ID in it to be unique per customer";
if ( ! $mutex->acquireLock($lockName, 60) ) {
    throw new Exception ("An operation is already running, please wait for it to finish and try again!");
}

// do the transaction data here, substract the amount, etc.

// once done, release the lock:
$mutex->releaseLock($lockName);


Please DO NOT REPLY directly to this email but go to StackOverflow:
http://stackoverflow.com/questions/44016197/google-app-engine-php-semaphores-or-alternatives-to-avoid-race-condition-for/44017076#44017076
Reply all
Reply to author
Forward
0 new messages