Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Ligonsker's avatar

How to prevent race condition when updating session value?

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

public function some_method(Request $request)
{
    // get previous last activity
    $session_last_activity = session('session_last_activity');
    // update last activity to now
    session(['session_last_activity'] => now());

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

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

The problem occurs when the session is expired, and there are quick multiple requests to the same page. Then, for those quick requests, each requests 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 was 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 and set a new key and create 2 records with 2 different keys, even though there was supposed to be only one.

Can I prevent that without using Cache? This specific system does not have Cache. And also on the session side, not the DB side with locks (if possible)

0 likes
17 replies
Snapey's avatar

Think you mis-diagnose

User with expired session clicks button twice, first request, there is no valid session matching the users cookie or session token. New session is created and the return response sends new cookie or token along with the response

Second request also presents the expired credentials. Framework has no idea who this request is from and so starts a new session, unrelated to the first and sends back cookie or token with response

Only the last received cookie/token is retained by the client

You cannot fix it without giving the client some additional client identification which does not expire

1 like
Ligonsker's avatar

@Snapey thank you, but this is not the session created by Laravel, it's a session I created that has 2 keys I need (the session_expiry_date and the some_value, but they are not related to the Laravel's auth session, the user is still authenticated even when my custom session might be expired)

Snapey's avatar

@Ligonsker i don't understand your code. Two requests will create new entries because for both the session expiry is <now(), you dont fix that so both requests follow exactly the same path?

1 like
Ligonsker's avatar

@Snapey Oh no sorry, I forgot one line, I edited the post, added session(['session_expiry_date' => now()]);.

Snapey's avatar

@Ligonsker no difference, session will have expired from the millisecond after you set it

1 like
Ligonsker's avatar

@Snapey But still, sometimes it creates 2 DB records with 2 different strings (and then the last string remains in the session). Perhaps one of the pages has some bad loading and sends 2 requests really really fast

Snapey's avatar

@Ligonsker you are not understanding me. You set the session end time to now(). Any request not coming in the same millisecond will follow the same path because session end time is in the past

1 like
Ligonsker's avatar

@Snapey You are right. Once again, I had a typo when copying the code: it's now()->subMinutes(10) in the condition and removed the previous one. many mistakes. I completely edited the code

Snapey's avatar

@Ligonsker ok, next logic fault.

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

    if ($session_last_activity < now()->subMinutes(10)) {

how can the if statement ever be true?

1 like
Ligonsker's avatar

@Snapey Because I first fetch the previous last activity in the line above (before I update the session):

$session_last_activity = session('session_last_activity');

So it should hold the previous data

Ligonsker's avatar

@Snapey thanks! Yes I noticed that there is the session blocking but because this specific system does not have Cache I can't use it (And I don't want to use the database driver for Cache). I might need to find another solution, but not sure it's possible

Snapey's avatar

@Ligonsker No cache? I wouldn't have thought that possible. Or do you mean you are using File ?

1 like
Ligonsker's avatar

@Snapey Oh yes, I am using File. I can then use Cache normally even though it's not Redis for example? Is File good enough? I never even thought of it as an option. You think it's good enough for a low traffic app?

Ligonsker's avatar

@Snapey Oh yes, I thought I could use the RateLimiter instead but that would not work either in this case

Ligonsker's avatar

@Snapey how about if I add the database session driver just for this api throttling? Then use the database driver only for this specific purpose, to limit the api calls, until I get redis to work? (And use the rest with the file driver, but that should be possible right? To use two different cache drivers)

However, if the default cache drivers remains file, how can I specify the database driver on the route I want to block?, Assuming the endpoint is /api for example

Please or to participate in this conversation.