skip to Main Content

In my Controller, I need to check if a session has expired, and if so, update the value:

public function some_method(Request $request)
{
    $session_expiry_date = session('session_expiry_date');

    if ($session_expiry_date < now()) {
        $some_value = Str::random(30);
        session(['some_value' => $some_value]);
    } else {
        $some_value = session('some_value');
    }

    session(['session_expiry_date' => now()]);


    // proceed to update or create some record on the DB
}

The problem occurs when the session is expired, and there are quick multiple requests to the same page. Then, for those immediate requests, each request creates a new random string and updates the session.

So the last request will have the last session value, however, by that time, the database created multiple records because, for each of these requests, the session expired for a moment.

For example, a user clicks example.com/page1 twice quickly while the session was expired. Now there are 2 requests that read the session as expired, set a new key, and create 2 records with 2 different keys, even though there was supposed to be only one.

How can I prevent that without using cache, or not on the DB side (i.e. with DB locks), but rather on the code side?

2

Answers


  1. Answer update based on your question update:

    One approach is to use a flag stored in the session itself to indicate whether a request is already in the process of updating the session.

    use IlluminateSupportFacadesSession;
    use IlluminateSupportStr;
    
    public function some_method(Request $request)
    {
        $session_expiry_date = session('session_expiry_date');
    
        if ($session_expiry_date < now()) {
            $lockKey = 'session_update_lock';
            
            // Check if the session update lock flag is set
            if (!Session::has($lockKey)) {
                // Set the lock flag to prevent other requests from updating the session
                Session::put($lockKey, true);
                
                $some_value = Str::random(30);
                
                // Update the session with the new value
                session(['some_value' => $some_value]);
                
                // Proceed to update or create the record on the DB
                // ...
                
                // Clear the lock flag to allow other requests to update the session
                Session::forget($lockKey);
            } else {
                // Another request is already updating the session, so retrieve the value
                $some_value = session('some_value');
            }
        } else {
            $some_value = session('some_value');
        }
    
        // Proceed with the rest of the code
    }
    
    
    
    Login or Signup to reply.
  2. To lock the session, you can use block on your route. The session locking will be acquired for this request whenever many simultaneous requests are incoming.

    So, if this request is for say example.com/page1, then the snippet would be:

    Route::get('/page1', function () {
        // your code
    })->block(5, 5);
    

    However, for this to be in effect, you must be using a cache driver that supports atomic locks. Quoting from the docs:

    To utilize session blocking, your application must be using a cache
    driver that supports atomic locks. Currently, those cache drivers
    include the memcached, dynamodb, redis, and database drivers. In
    addition, you may not use the cookie session driver.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search