PHP & Sessions: Is there any way to disable PHP session locking?
You don't want to disable it... If you do, you'll potentially run into all sorts of weird issues where you login on one window, be logged out on another and then wind up in an inconsistent state... The locking is there for a reason...
Instead, close the session very early if you know you are not going to write to it in that request. Once you start it, you'll be able to read from it for the whole request (unless you re-start it, or do some other special things) even after you call session_write_close
. So what you would do, is check the request to see if it's a write request, and if it's not, just close it right after opening it. Note that this may have some adverse affects if you later try writing to the session (For Captcha or CSRF protection or something else), so use with caution...
But instead of trying to get around it, I would put my effort into either shortening the request lengths (to reduce lock contention), or making cookieless requests for those requests that don't need the session at all...
I had a report page that took over 2 minutes (over 80 AJAX requests). I got it down to under 30 seconds by removing session-locking. Yes, heaven forbid you remove file locking, because then you have race conditions. And if you don't understand race-conditions and you don't know what the effect would be on your sessions...then do NOT remove file-locking. However, if you, knowing what data that is in your sessions, and knowing what race-conditions do, feel that there is no data that could be adversely affected by race conditions which would create a bug...you know your environment better than anyone else does, so go for it.
MySQL, REDIS, and Memcache
Also, note that if you switch to MySQL for session management, there's a 99% chance, IMHO, that you do NOT lock the row from the time you read to the time you write. So, using MySQL you still have the same race conditions (or locking problems if you decide to lock the row).
Based on the information I could find, people who use PHP Redis are using a non-locking application that is prone to race conditions...as per the following thread...and they cite speed as one of the reasons they like this "feature":
https://github.com/phpredis/phpredis/issues/37
Memcached didn't support session locking until version 3.0.4...so it was also--initially--prone to race conditions.
Clearly, with the success of these options, race conditions are not the biggest problem that programmers face.
Ultimately the Issue Is
ALL concurrent requests will ALWAYS be subject to race-conditions UNLESS you do file locking, at which point they are not concurrent requests anymore.
The important thing about Sessions and Locking vs. Concurrency and race conditions is to know your application, know if a race condition could break your application...and devise a solution that fits YOUR application. If all you do is store the userId in the session and read it on all subsequent requests, you probably don't need to worry about race conditions. If you store an elaborate history of data that will break if things happen out of order or data might be lost, then lock the file between reading and writing and try to do your write as quickly after the read as possible to limit the amount of time the file is locked.
The Best Option
A session-less API will, however, be much better for concurrent requests. However, if you don't have time to refactor to such an API...then read-on.
A Stop-Gap Solution to keep using PHP session files and stop locking
To continue using PHP sessions the default way, to stop locking, and basically have a very quick solution to a complex problem, you can implement the PHP website's example implementation of a SessionHandler.
I have the below code running in a production environment for a site with tens of thousands of connections per minute, and I haven't had any problems with race conditions yet, but I also don't store data that a race-condition could break. This code, as I said, got one report from over 2 minutes down to under 30 seconds...and it took a couple of minutes to implement. No MySQL schema to create, no Memcache or Redis to install.
This is, to a letter, the example implementation provided on PHP's documentation (http://php.net/manual/en/class.sessionhandlerinterface.php), and it doesn't lock the session file when it reads it.
NOTE As Anther pointed out in this comments, this will not work in a distributed system unless you save the file to a single server.
<?php
class MySessionHandler implements SessionHandlerInterface
{
private $savePath;
public function open($savePath, $sessionName)
{
$this->savePath = $savePath;
if (!is_dir($this->savePath)) {
mkdir($this->savePath, 0777);
}
return true;
}
public function close()
{
return true;
}
public function read($id)
{
return (string)@file_get_contents("$this->savePath/sess_$id");
}
public function write($id, $data)
{
return file_put_contents("$this->savePath/sess_$id", $data) === false ? false : true;
}
public function destroy($id)
{
$file = "$this->savePath/sess_$id";
if (file_exists($file)) {
unlink($file);
}
return true;
}
public function gc($maxlifetime)
{
foreach (glob("$this->savePath/sess_*") as $file) {
if (filemtime($file) + $maxlifetime < time() && file_exists($file)) {
unlink($file);
}
}
return true;
}
}
In PHP 5.4+, using it is as simple as setting the handler before you start your session:
$handler = new MySessionHandler();
session_set_save_handler($handler, true);
session_start();
For lower versions of PHP, you can still do it via function calls...see the PHP docs.
You can restart the session by recalling session_start() after session_write_close(). However this will cause multiple SIDS. I resolve this by removing the multiple SIDS from the header before the output is flushed.
See this example: https://gist.github.com/CMCDragonkai/6912726#file-nativesession-php-L254