Session locking: Non-blocking read-only sessions in PHP

I ran into an excellent article titled PHP Session Locks – How to Prevent Blocking Requests and it inspired me. Judging by the comments, it seems that not everybody fully understands what session locking is, how it works, and why it is necessary. This post tries to clear these things up and also gives you a dirty way of speeding up your AJAX calls significantly.

What session locking is

To understand this, we first need to know that a web server does not run your PHP code in a single process. Multiple worker processes are running concurrently and they are all handling requests. Normally, visitor requests of your web page are serialized. This is also where HTTP persistent connections (a.k.a. keep-alives) come into play. By keeping the connection open for the requesting of all the assets of the page, the connection overhead is avoided. Browsers are quite smart and will always try to serialize requests for HTML pages. For the assets (images, scripts, etc.) on the page there is another strategy. The browser will download multiple assets in parallel from each unique hostname it sees referred in the HTML. It can do this by opening multiple TCP connections or by pipelining. When a browser thinks it is downloading assets it may download these for a single visitor in parallel. Session locking avoids this parallelism (by blocking) to provide reliable access to the session data in this situation.

How session locking works

This is quite easy: When you call “session_start()” PHP will block (wait) in this call until the previous script has called “session_write_close()”. On Linux it does this by relying on the “flock()” call. This is an advisory locking mechanism that blocks until the lock is released. NB: This locking time is not counted as part of the “max_execution_time” (see: set_time_limit()).

Why session locking is necessary

Session locking prevents race conditions on the shared memory that is used to store session data. Every PHP process reads the entire session storage when starting and writes it back when closing. This means that to reliably store the logging-in of a user (which is typically done in the session data) you must make sure no other process has read the session data and will overwrite your data after you have written it (since the last write wins). This is needed even more when using AJAX or IFrames since the browser considers those loads to be assets and not HTML pages (so they will be parallelized).

Read-only sessions to the rescue

Many websites use AJAX calls to load data. While retrieving this data we would like to know whether the user logged in to deny access if  needed. Moreover, we would not like the loading of this AJAX data to be serialized by the session locking, which slows down the website. This is where the following (arguably dirty) code comes into place. It will allow you to gain read-only access to the session data (call it instead of “session_start()”). This way you can check permissions in your AJAX call, but without locking, thus not blocking and serializing the requests. It may speed up your PHP powered AJAX website significantly!

            function session_readonly()
            {
                    $session_name = preg_replace('/[^\da-z]/i', '', $_COOKIE[session_name()]);
                    $session_data = file_get_contents(session_save_path().'/sess_'.$session_name);

                    $return_data = array();
                    $offset = 0;
                    while ($offset < strlen($session_data)) {
                        if (!strstr(substr($session_data, $offset), "|")) break;
                        $pos = strpos($session_data, "|", $offset);
                        $num = $pos - $offset;
                        $varname = substr($session_data, $offset, $num);
                        $offset += $num + 1;
                        $data = unserialize(substr($session_data, $offset));
                        $return_data[$varname] = $data;
                        $offset += strlen(serialize($data));
                    }
                    $_SESSION = $return_data;
            }

I think this call should be added to the next PHP version. What do you think? Let me know in the comments.

Share

8 thoughts on “Session locking: Non-blocking read-only sessions in PHP”

  1. There was a feature request for read-only sessions:
    https://bugs.php.net/bug.php?id=8978
    but for some reasons it has been closed (cancelled?).

    Considering your solution I must say that code is not just dirty – it’s unreliable.
    There are at least three different “formats” of session serialization in php, see the session.serialize_handler in php.ini
    It can be “php”, “php_binary” or “wddx”.
    So at least you must have a support for all of them, and some day in future php devs can add another format which will broke your solution.

    The simplest way to get “readonly-like” session is:
    session_start();
    READ SESSION DATA
    session_write_close();

    A true solution would be to implement your own session handling mechanism, but it will take a lot more effort.

  2. @lurker: Thank you for your comment. I did not know that others proposed this already.. this is great! I agree this is dirty and it should support all methods. But the problem with the “simplest way” is that the read-only sessions still block until any concurrent read-write session has ended. And the idea is to make it non-blocking.

  3. @Arnold: Thank you! I tried, but it relies on calling session_start() first. Or did you find a way around that?

  4. @Yury: Sounds goods at first, but it gives you unreliable sessions, since it does not support session locking:

    https://github.com/nicolasff/phpredis/issues/37

    NB: Not having session locking is NOT the same as non-blocking sessions. Session locking is needed for reliable sessions when writing to the session. Session locking is not needed when only reading from the session.

Leave a Reply

Your email address will not be published. Required fields are marked *