My application is implemented with php/laravel and MySQL database. In this system, users can deposit money to wallet. I have a wallet_balance column in the users table. When users deposit funds into their wallet, I update that column as follows:
public function topup(Request $request)
{
$user = auth()->user();
$deposit_amount = $request->deposit_amount;
//e.g.
//$deposit_amount = 100
//$user->wallet_balance = 50
$user->wallet_balance = $user->wallet_balance + $deposit_amount;
$user->save();
// After saving I expect $user->wallet_balance to be 150 which works perfectly.
}
There are some services that users are charged where money is deducted from their wallet (in another function). For example:
public function chargeService(Request $request)
{
$user = User::findOrFail($request->user_id);
$service_fee = $request->service_fee;
//$service_fee = 30
//$user->wallet_balance = 150
$user->wallet_balance = $user->wallet_balance - $service_fee;
$user->save();
// After saving I expect $user->wallet_balance to be 120 which works perfectly.
}
After the two transactions, the user's wallet balance should be 120. However, on very rare cases the two transactions might happen concurrently. That means in the deposit transaction, the initial wallet balance is 50 and also for the service fee transaction the initial wallet balance is 50 (because they queried the database at the same time before any of the two updated the wallet balance). Here is the danger. The first transaction will have the resulting wallet balance as 150 (50 + 100), and the second will have it as 20 (50 - 30). This leaves the wallet balance of the user as either 150 or 20, depending on which operation updates the user's wallet balance last.
Now, my question is, how can I approach my wallet system so as to avoid this stinking issue. I will appreciate so much guys.
You can suggest a better way to phrase the question.