You are already validating against active sessions. I recommend you create a form request class to abstract the validation and subsequent checks you're performing (balance and active status). You have access to the currently authenticated user in a form request class, so you could promote your balance check to authorisation level or stick with custom validation rules.
Mar 10, 2025
6
Level 1
How to prevent race condition in this case laravel 11
I have a table named gym_sessions it has user_id | status columns.
status column can contains: 1 ==> active; 2 ==> completed; 3 ==> declined;
Conditions:
- User must have sufficient balance to start new session.
- User can't have two active sessions at the same time.
- User can have many sessions of status: 2 | 3;
This is my method for creating new session for a user:
// Start new gym session.
public function startGymSession(Request $request){
$validator = Validator::make($request->all(),[
'gym_id' => ['required', 'exists:gyms,id'],
'payment_method_id' => ['required', 'exists:payment_methods,id'],
'gym_pricing_id' => ['required',
Rule::exists('gym_pricings', 'id')->where(function ($query) use ($request) {
$query->where('gym_id', $request->input('gym_id'));
}),
],
]);
if($validator->fails()){
return $this->sendError('Validation error!');
}
$user = Auth::user();
// Check if the user have enough balance to start a gym session.
$userbalance = $user->wallet_balance;
$gymPricing = GymPricing::find($request->gym_pricing_id)->first();
if($userbalance < $gymPricing->price){
return $this->sendError('Insufficient balance.');
}
// Check if the user have active gym sessions.
// If the user have active gym sessions, he can't start a new one.
// Prevent race condition.
/** @var \App\Models\User $user */
if($user->gymSessions()->where('status', 1)->count() > 0){
return $this->sendError('You have an active gym session.');
}
$now = Carbon::now();
// Start the gym session.
$gymSession = GymSession::create([
'user_id' => $user->id,
'gym_id' => $request->gym_id,
'payment_method_id' => $request->payment_method_id,
'gym_pricing_id' => $request->gym_pricing_id,
'entry_time' => $now,
]);
if($gymSession){
return $this->sendResponse([], 'Gym session started.');
}
return $this->sendError('Failed to start gym session.');
}
Attack Scenario:(Race Condition) If the user sent multiple requests in parallel, the system will create many sessions with status 1.
I want to know how to prevent this problem on application level (using Laravel methods) not on database level?
Env
Laravel 11
MySql
php 8.2.8
Please or to participate in this conversation.