Reputation: 2091
I have an API which is receiving two requests at exactly the same time. It results in two transactions being run at the same time as well, and so in duplicating the data. The pseudo code of my endpoint is:
public function myEndpoint(Request $request)
{
$newOrderID = $request->get('order_id');
DB::beginTransaction();
try {
$ordersCount = Order::where(['id' => $newOrderID])->count();
if ($ordersCount === 0) {
Order::create(['id' => $newOrderID]);
$stat = Stat::where(['date' => Carbon::now()])->first();
$stat->orders_count = $stat->orders_count + 1;
$stat->save();
}
DB::commit();
} catch (Exception $e) {
DB::rollBack();
}
}
So the endpoint is a transaction that:
The orders table does not have the unique key. It checks for the duplicates, and if there is an order ID already, it should not do anything.
The problem is - two transactions running at the same time, there is no duplicate yet, and the data gets duplicated, so I end up having two rows with the same order ID.
Setting the unique key will not work, because the statistics table will always get incorrectly incremented anyway.
Is there a nice solution to this problem?
Upvotes: 0
Views: 1372
Reputation: 1877
You can make a hash and lock incoming requests if same order_id is already processing
Upvotes: 1
Reputation: 643
You can use mysql trigger to resolve the duplicate entry. Here is the sample trigger. You don't have to check if exist
in php code.
DROP TRIGGER IF EXISTS order_transaction_before_insert;
DELIMITER $$
CREATE TRIGGER order_transaction_before_insert
BEFORE INSERT ON order FOR EACH ROW
BEGIN
DECLARE msg varchar(128);
DECLARE foundCount INT;
SET foundCount = (
SELECT COUNT(*)
FROM order
WHERE
order_id = NEW.order_id
);
IF( foundCount > 0) THEN
SET msg = CONCAT('DuplicateEntryError: Trying to insert a duplicate value in order table for driver: ', NEW.order_id);
SIGNAL SQLSTATE VALUE '45000' SET MESSAGE_TEXT = msg;
END IF;
END; $$
DELIMITER ;
Upvotes: 0