PHP Session Timeout

The items utility has had a nasty habit of timing out users after a relatively short amount of time. In the past I tried extending the cookie timeout, but that didn’t really seem to help. The problem has been troublesome because it seems to happen almost randomly.

A little web research has led me to another conclusion as to the source of the problem: session data garbage collection.

Cleaning up

The purpose of garbage collection is to ensure that old session data isn’t left laying around on the system. PHP performs garbage collection at semi-random intervals based on the values of the session.gc_probability and session.gc_divisor php.ini declarations, with default values of 1 and 100 respectively. With the default values this means garbage collection will be called on average 1% (1/100) of the times that a PHP session is started. In terms of our web-based utilities, that means 1% of the time that a PHP-based page is accessed. Right now, with the amount of data entry going on in the utility the chance of PHP performing garbage collection is pretty high.

PHP uses the age of the session data when determining what to clean up. By default that age is 24 minutes from the date/time of last modification of the session data file. In the items utility the session data is only written once, meaning that as soon as 24 minutes after initial log in the user’s session could be destroyed. This, of course, depends on when garbage collection occurs.

One final important piece of information, session data is by default written to disk in the location specified by the session.save_path php.ini declaration (/tmp by default). This means that all sessions’ data are stored in this location. So not only is the items utility affected by the high rate of page churn right now, any PHP page running on the server that uses sessions is affected.

Extending the session

I have not definitively identified garbage collection as the source of the problem, but if it is then the solution is a change to all three parameters specified above. This is something that we want to do only for the items utility, and fortunately this is easily done in the site’s Apache .conf file:

<Directory /path/to/items/utility/files>
php_value session.gc_maxlifetime 7200
php_value session.gc_probability 5
php_value session.gc_divisor 1000
php_value session.save_path /session/file/save/path

A quick run-down of the configuration:

  • the changes to session.gc_probability and session.gc_divisor set the probability of PHP invoking garbage collection to .5%
  • the change to session.gc_maxlifetime increases the age limit of the session data to two hours
  • the change to session.save_path isolates the items utility session data from other sessions

This last point is important. If we modify the garbage collection parameters without changing where the session files are stored they may still be destroyed based on the defaults. PHP doesn’t record session parameters (like data file expiration) within the session data file, so session parameters are determined by the running script. If garbage collection is initiated any sessions stored in the same location as those for the currently running script will be parsed for clean-up based on the parameters of that script.


Keep it fresh

This is just a bit more information on the 24 minute session expiration I mentioned above.

While investigating how the server settings were affecting sessions I had a thought that maybe the utility itself might not be interacting with the session in a way that PHP was able to tell that the session was still active. In my research I noticed that the way PHP determines active sessions has changed over time. In older versions of PHP the garbage collector would look at the access time of a file to determine session activity. In the more recent versions of PHP the garbage collector looks at the modification time.

I began to wonder if part of the reason the session timeouts have only recently been a problem is because we weren’t updating the session data on a regular enough basis to ensure the modification time of the data file is “fresh.” Quick testing proved me correct. Session data (and thus the data file modification time) was only written on initial log in. This type of setup causes a user’s session to become stale 24 minutes (the session.gc_maxlifetime default) after log in, no matter how active the user is. To avoid this problem in the future I updated the authentication code so that it updates a last-access-time variable in the session data. This ensures that the user’s session doesn’t go stale so long as the user is active (moving from page to page in the utility).