I have a system put behind an authentication – using php sessions.
The problem is that, seemingly randomly, session_start() is (successfully) executed between 40 and 130 seconds.
The only way I’ve been able to reproduce the issue is by logging out (session_unset()
followed by session_destroy()
, then redirect to login page) and logging in again (session_start()
followed by some session variables being set).
At that point, the page loads instantly. But as soon as I load another page from that one, any page, it takes a lot of time. Once it completes, I can load all pages just fine instantly.
There is not a difference between the page that loads instantly and the one that takes a lot of time – In both cases, there is just
<?php
session_start();
die('session started');
The issue has not always been there – it started a few weeks ago, likely following some update by the hoster. It can be reproduced on any browser, by anywhere.
Things I’ve tried:
- following
session_start()
withsession_write_close()
when I don’t need to set anything anymore - changing the session save path
session.lazy_write = 0
andmemcached.sess_locking = Off
– not even using memcached but still tried- checking server logs and putting up profiling on the php side – the server hands the request to php instantly, it hangs on
session_start()
for some time, then suddenly completes and the script executes instantly.
What bugs me is that after the initial delay is completed, everything is smooth as butter. Which makes me think about some kind of lock being released after around a minute and letting the script run properly.
Things I haven’t tried:
- Switching session handling from the default, file-based one, to db/memcached/redis/anything else – this would probably solve it, but it doesn’t exactly sit right with me
EDIT: Cleaned up version of the actual code
// login.php
<?php
if (login()) {
session_start();
$_SESSION['login'] = true;
/**
* some more session variables being set, taken from the db - they're not too many and
* they're pretty simple strings and booleans
*/
header('Location: home.php');
exit();
}
After this, the user gets redirected to home.php – a pretty basic page, just showing some info (part of them taken from sessions)
but not writing anything into the session
// home.php
<?php
session_start();
// well, the rest of the code
Now, if I try to navigate to any other page, session_start() is SLOOOOOW. For simplicity’s sake, I’m gonna use the logout
page as an example – let’s type site.com/logout.php
in the url bar and press enter.
Then, wait a minute for session_start() to complete successfully;
logout.php is as simple as:
// logout.php
<?php
session_start();
session_unset();
session_destroy();
header('Location: index.php');
exit();
After waiting for a minute, we’re succesfully logged out. We can login again and repeat the same exact scenario,
on the sequent page load after home.php.
Even reloading home.php itself reproduces the same bug. It’s always on the second page load after login.
2
Answers
While writing, I noticed the problem and got it sorted out.
There was a point in the home.php page where an AJAX call to a pretty slow script was being made - that one used sessions too so hung every other page load until it finished.
So in case anyone ever stumbles upon this:
Check the WHOLE script, especially the Network tab of your dev tools.
There might be some other long running request delaying all the others.
The reason is PHP will not release session until the request is completed. So if any previous request is pending then the session is locked and the next request is blocked at the point of session_start() command. To avoid this issue, if you are not writing into session, once the session data is read to the local variable, then you can use session_write_close(); to prevent the session lock. And if you have to write again to the session then you have to use session_start() before.