'Laravel Database Transactions with Locking for Flash Sale system (Concurrent Requests)
I'm very new to this concept of Database locking/isolation. I watched some videos on the Topic of ACID properties.
The concept is pretty much clear, but I'm not sure how I can implement this in Laravel.
I'm trying to make a "Flash Sale" system, like suppose 50,000 users trying to order 1,000 products, where most requests would be concurrent.
As I understood DB::transaction will ensure if anything goes wrong, then everything will be rolled back, but it won't solve the concurrent requests problem, right?
I read about laravel's Pessimistic Locking - Laravel Docs, but very confused between ->sharedLock() and ->lockForUpdate()
I couldn't find much detailed article or vids in this topic, even the Laravel documentation is very short in this topic.
How do I use these in my case. I think I would be able to explain better by pasting the code and explaining each line via comments.
Thank you so much in advance.
So, here it is...
DB::transaction(function () use ($request, $productIdsQty, $address) {
// Create the Order
$order = $request->user()->orders()->create([
'payment_method' => $request->payment_method,
]);
$isOnlinePayment = $order->payment_method != 'cod';
// assign Address for the Order
$order->address()->create($address->toArray());
// Here the problem occurs, suppose when this was queried that time all products had enough stock.
// but when we later attaching these products to our Order,
// what if one or more items had some change in stock at that point.
$products = Product::whereIn('id', Arr::pluck($request->products, 'id'))->get();
foreach ($products as $product) {
// User demanded qty
$qty = $productIdsQty[$product->id];
// max_qty is just a accessor
// which returns the qty user can order right now
// based on available stock and admin set max_qty_per_order - whichever is lower,
// I'm using this line as a where clause.
// I dont know if this is the right way, but it just works for now
$product = $product->max_qty >= $qty ? $product : null;
// Decrement the product stock
if ($product->stock_mode == 'manual') {
$product->decrement('stock', $qty);
}
// Add the product to ordered items
$order->items()->create([
'qty' => $qty,
'product_id' => $product->id,
'unit_price' => $product->price,
'unit_mrp' => $product->mrp,
'status' => $isOnlinePayment ? 'payment_pending' : 'order_placed',
]);
} // Products foreach loop ends
// Process prepaid orders
if ($isOnlinePayment) {
// Add payment expiry for prepaid orders
$order->update(['payment_expires_at' => now()->addMinutes(20)]);
return Inertia::location(route('order.pay-now', $order->id));
}
});
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
