5

We're considering moving our php session data to redis.

The setup looks simple. Just set the following in php.ini and restart apache. It should be all set.:

session.save_handler = redis
session.save_path = "tcp://host1:6379"

If possible I would like our users not to notice the migration. Is it possible to move the session data to redis without losing any existing session data?

5
  • 1
    Possible, yes, easy, not so much. Afaik phpredis does not have a migration script, so you'd have to write one yourself. You may want to take a look at Cm_RedisSession's script that does something similar for that redis module. Commented Sep 29, 2012 at 9:34
  • Thank you. I think your comment answers my question. I couldn't find a script like that myself. Looking at the script a few functions needs to be provided by the library in use. Probably writeRawSession() is a requirement before we can code a migration script. I've submitted this as an issue to phpredis. If you don't mind, write your comment as an answer so I can mark it accepted. Commented Sep 29, 2012 at 12:49
  • 1
    Actually a custom script for this should be easy to build. Basically you would want to iterate through each session file and create a new string in redis under PHPREDIS_SESSION:filename where filename is the name of the session file, which also happens to be session id, and set this key to the contents of the file 1:1. Optionally you may also set a custom expiration time afterwards. Commented Sep 30, 2012 at 0:10
  • 1
    Or even better, you could make the change progressively by staying with file based sessions for now but populating redis in real time as session data is being used until the amount of sessions stored in redis looks good enough to do the switch. This could be achieved by doing something like $redis->set("PHPREDIS_SESSION:".session_id(), session_encode()); right before each script ends. (this may add a little bit of overhead depending on the amount of data in session and how session_encode works) Commented Sep 30, 2012 at 0:27
  • @Mahn Thank you for the helpful comments. I'll try this method. This would actually remove the need for a php script. Commented Oct 2, 2012 at 21:17

5 Answers 5

8

There is no out-of-the-box solution available right now for what you are asking, but writing a custom script for this task can actually be fairly simple.

Esentially, phpredis stores session data in redis as strings with the following key name format: PHPREDIS_SESSION:$sessionid, where $sessionid is the php id of the session, the one retrievable via session_id(). The session data is "encoded" as a php-session serialized variable (which is a slightly different format to the common php serialize/unserialize, see session_encode).

Now that we know this, there are two possibilities to migrate session data stored in files:

  • Iterate through every session file (the actual path is set at session.save_path in your php.ini), read the data and write it back to redis. The files themselves store the php-session serialized representation of the session data, which means the content can be copied as it is directly to redis, and the filenames have the following pattern: sess_$sessionid, where $sessionid is, you guessed it, the id you'll want to use for your redis keys.

  • Migrate the data progressively by staying with file based sessions for now, but populating redis in real time as session data is being used, until the amount of sessions stored in redis looks good enough to do the switch. This could be achieved by doing something like:

    $redis->set("PHPREDIS_SESSION:".session_id(), session_encode());

    Right before each script ends. This method may add a little bit of overhead depending on the amount of data in session and how session_encode works.

Sign up to request clarification or add additional context in comments.

Comments

3

Just created such a script in bash and added to my repo.

https://github.com/renasboy/php-redis-migrate-sessions

Comments

3

If you are using symfony, you can use a command like this:

yml configuration:

parameters:
    redis_address: "localhost"
    project_name : "ACME_"

snc_redis:
    clients:
        default:
            type: predis
            alias: default
            dsn: redis://%redis_address%
            logging: '%kernel.debug%'
        session:
            type: predis
            alias: session
            dsn: redis://%redis_address%/1
            logging: true
    session:
        client: session
        prefix: '%project_name%PHPREDIS_SESSION'
        ttl: 7776000 # 90 days

symfony command:

<?php

// Command:  app/console acme:migrate:session:files:to:redis --env=dev
namespace Acme\AppBundle\Command;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Finder\Finder;

class MigrateSessionFilesToRedisCommand extends ContainerAwareCommand {

protected $env;

protected function configure() {
    $this->setName('acme:migrate:session:files:to:redis')
        ->setDescription("Migrate Session Files To Redis")
        ->setHelp("Migrate Session Files To Redis");
}


protected function execute(InputInterface $input, OutputInterface $output) {

    $sessionPath = realpath( sprintf('%s%s', $this->getContainer()->getParameter('kernel.root_dir'), '/sessions') );
    $prefix      = 'ACME_PHPREDIS_SESSION';

    $redis = $this->getContainer()->get('snc_redis.session');

    $finder = new Finder();
    $finder->files()->in($sessionPath);

    foreach ($finder as $file) {

        $realPath = $file->getRealpath();

        $sessionId = str_replace( 'sess_', '', $file->getRelativePathname() );
        $redis->append( sprintf('%s:%s', $prefix, $sessionId) , file_get_contents( $realPath ) );
    }

}

}

Note: Replace "ACME" with your Project ID/Name and Set The right Session Path where files are stored.

Comments

2

Possible, yes, easy, not so much.

AFAIK, phpredis does not have a migration script, so you'd have to write one yourself. You may want to take a look at Cm_RedisSession's script that does something similar for that redis module.

Comments

1

Here is my super simple script for importing session data to redis:

#!/bin/bash


export REDISCLI_AUTH=my-supper-strong-password-4-redis-server
TTL=$(( 24 * 3600 ))

for i in sess_*; do
    ex=$(( $(date +%s) - $(stat -c %Y "$i") + $TTL ))
    k="PHPREDIS_SESSION:${i:5}"
    v=$(cat "$i")

    echo "SET $k of len:${#v} EX $ex"
    redis-cli SET "$k" "$v" EX $ex
done

I did not test it thoroughly, so use it caution.

1 Comment

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.