I'm a programmer specialising in performant and scalable systems using PHP and Ruby and cooking


Published:

Storing sessions in memcache with Symfony2 and PHP

When load balancing your site between multiple application servers sessions and making sure people stay logged in is always a concern. This can sometimes be solved by using sticky sessions and making sure people always hit the same server, but this involves configuring the load balancer, adding more weight to the response (tracking cookie) and losing the ability to actually load balance requests.

To solve this we've been using memcache to store sessions. This can be shared between multiple servers or a single central session server. It's also pretty easy to make sure the request always hits the same server using the hash.

For this I'm going to be using the Memcache PHP extension, although I tend to use the more recent Memcached extension however but that is a little more cimplicated to setup.

The simplest way to configure this for most applications is to use the session storage settings in PHP itself. This involves a couple of changes to your php.ini:

session.save_handler = memcache  
session.save_path = "tcp://127.0.0.1:11211"  

If you are editing a clean ini you will see (on ubunutu anyway) that the save handler is normally set to "files" and that the save_path isn't configured as it uses the default setting of the system temp directory.

Now we get to Symfony2, try this out and you'll find your application is still using filesystem storage for session. This took me a while to work out but as it turns out the Symfony2 framework components actually configure the session storage and handler itself. Whilst annoying at first it starts to make sense in the context of multiple sites running on one server and needing different session settings and when you don't want to blanket configure sessions via the SAPI for all applications.

Symfony2 ships with a few different handlers BUT they are NOT plug and play. This means that in theory you should just be able to drop in a different session handler service and boom your sessions are now stored in memcache. However because these services are not configured by default you need to configure them yourself first (bit frustrating tbh).

First step is to add our memcache server settings to the parameters.yml:

session_host: 127.0.0.1  
session_port: 11211  
session_prefix: sess_  
session_lifetime: 86400  

Next we need to configure our services to use these settings and to enable us to tell the framework to use our desired handler (Using my bundles services config):

<?xml version="1.0" ?>  
<container xmlns="symfony.com/schema/dic/services"  
    xmlns:xsi="w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="symfony.com/schema/dic/services symfony.com/schema/dic/service..">

    <services>
        <service id="session.memcache" class="\Memcache">
            <call method="addServer">
                <argument>%session_host%</argument>
                <argument>%session_port%</argument>
            </call>
        </service>

        <service id="session.handler.memcache" class="\Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcacheSessionHandler">
            <argument type="service" id="session.memcache">
            <argument type="collection">
                <argument>%session_prefix%</argument>
                <argument>%session_lifetime%</argument>
            </argument>
        </service>
    </services>
</container>  

Now all that's left to do is tell the framework to use our new handler in your apps config (app/config/config_prod.yml):

framework:  
    session:
        handler_id: session.handler.memcache

I'd really like to see these additional handlers available as services by default allowing easy drop in of different session handlers.