MTM
MTM

Reputation: 153

How to check a condition if it was true then insert some thing at race condition

We have an api function which check a condition on database with a select-query then if it was true we want just for one time insert some thing to database for example inserting to database that insertion done. Problem is when we call multiple times this api-function concurrently race condition happen, in another words assume we call this function 2 times, first request check the condition it's true then second request check that and it's true again so their do insert to database. But we want to when we check condition no other one can check it again until we do insertion.

We use php/Laravel and know about some ways like using insert into ... select or using some thing like replace into ... and so on.

$order = Order::find($orderId);
$logRefer = $order->user->logrefer;
if (!is_null($logRefer) && is_null($logRefer->user_turnover_id)) {
     $userTurnover = new UserTurnover();
     $userTurnover->user_id = $logRefer->referrer_id;
     $userTurnover->order_id = $order->id;
     $userTurnover->save();

     $logRefer->order_id = $order->id;
     $logRefer->user_turnover_id = $userTurnover->id;
     $logRefer->save();
}

If logrefer not found set it and corresponding user-turnover just for one time. We expect to see just one user-turnover related to this order but after running it multiple time concurrently we see multiple user-turnover has inserted.

Upvotes: 1

Views: 590

Answers (2)

Michel Feldheim
Michel Feldheim

Reputation: 18250

The database should have a unique key on columns expected to be unique, even if some mechanism in the code prevents duplicates.

Wrap connected queries into a transaction which will fail and roll back in a race event condition

try {
    DB::transaction(function() {
        $order = Order::find($orderId);
        ...
        $logRefer->save();
    });
} catch (\Illuminate\Database\QueryException $ex) {
    Log::error(“failed to write to database”);
}

Upvotes: 1

BUcorp
BUcorp

Reputation: 359

I usually take advantage of transaction when operations need to be sequential but i think that in your case the situation it's a bit complex due to the fact that also the condition need to be evaluated conditionally if the function it's running. So the idea that i can give you it's to have on the database a variable (another table) used as semaphore which allow or not to perform actions on the table (condition gived by the fact that you set or unset the value of the semaphore). I think as good programmer that semaphore are useful in a lot of cases of concurrential functions.

Upvotes: 1

Related Questions