sdcapp's avatar
Level 5

Issue with Cache Lock for cart items

I have a class called "MakeOrderAction" and it contains the execution method. before running the execution code i have run self::makeAtomicLock($cart, $lockTime); method, and this method make a lock for cart item's to avoid over stock orders and the race condition.

protected static function makeAtomicLock(Cart $cart, int $lockTime): array
{
    $locks = [];
    foreach ($cart->items as $item) {
        if($item->model instanceof Product){
            /*
             * if product has code type then lock the code
             * else lock the product
             * */
            if($item->model?->hasCodesType()){
                // Lock for update to avoid race conditions
                $availableCode = $item->model->promo_codes()
                    ->whereDoesntHave('users')
                    ->where('status', 1)
                    ->lockForUpdate()
                    ->first();

                if (!$availableCode) {
                    throw new Exception(__('Product is out of stock'));
                }
            }
            // lock item for update available_usage_count
            $item->model->lockForUpdate();
            // lock using product id
            $productLockKey = "product_lock_{$item->model_id}";

            if(!Cache::lock($productLockKey)->get(0)){
                throw new Exception(__('Product is not available at this moment. Please try again.'), 422);
            }

            $locks[$productLockKey] = Cache::lock($productLockKey, $lockTime);
        }
    }

    return $locks;
}

The problem here is that when the second order starts and the lock is still not released, it returns "Product is not available," but I need to make the user wait some seconds while the lock is released.

I'm trying to use the block, but I've faced another issue: the first request is waiting for the second request to finish instead of the first order finishing and releasing the locks, then the second one starting!

What should i do!?

0 likes
1 reply
LaryAI's avatar
Level 58

To address the issue of handling cache locks for cart items and ensuring that users wait for a lock to be released, you can use the block method provided by Laravel's cache lock system. This method allows you to specify a maximum number of seconds to wait for a lock to become available.

Here's how you can modify your makeAtomicLock method to use the block method:

Explanation:

  1. Block Method: The block method is used to wait for a lock to become available. It takes a parameter that specifies the maximum number of seconds to wait. If the lock is acquired within this time, it returns true; otherwise, it returns false.

  2. Lock Time: Ensure that the $lockTime is set to a reasonable value that allows the first request to complete and release the lock before the second request times out.

  3. Exception Handling: If the lock cannot be acquired within the specified time, an exception is thrown, indicating that the product is not available.

By using the block method, you ensure that subsequent requests wait for the lock to be released by the first request, thus preventing race conditions and overstock issues.

Please or to participate in this conversation.