skip to Main Content

We’re using Memcached to store Laravel’s cookie-based session at the moment but want to use Redis throughout for session and cache for consistency.

Is there a way to migrate all user sessions from Memcached to Redis without logging out the users?

2

Answers


  1. Chosen as BEST ANSWER

    I was able to migrate the sessions without logging out the users. I created an artisan command for the purpose:

    <?php
    namespace AppConsoleCommands;
    use Memcached;
    use Redis;
    use IlluminateConsoleCommand;
    class MigrateSessionToRedis extends Command
    {
        /**
         * The name and signature of the console command.
         *
         * @var string
         */
        protected $signature = 'migrate:session';
        /**
         * The console command description.
         *
         * @var string
         */
        protected $description = 'Migrate sessions from Memcached to Redis';
        /**
         * Create a new command instance.
         *
         * @return void
         */
        public function __construct() #NOSONAR
        {
            parent::__construct();
        }
        /**
         * Execute the console command.
         *
         * @return mixed
         */
        public function handle()
        {
            $this->info('Starting session migration..');
            $memcached = new Memcached();
            $memcached->addServer(env('MEMCACHED_HOST'), 11211);
            $sessions = $this->getMemcachedKeys(env('MEMCACHED_HOST'));
            if (! is_array($sessions)) {
                $this->error('Could not retrieve sessions from Memcached');
                exit(1);
            }
            foreach ($sessions as $session) {
                $value = $memcached->get($session);
                if ($value) {
                    $object = unserialize($value);
                    $valueForRedis = serialize(serialize($object));
                    // Set session in Redis with session expiry lifetime
                    Redis::set("{$session}", $valueForRedis, 'EX', config('session.lifetime'));
                }
            }
            $this->info('Finished session migration.');
        }
        /**
        * Get all memcached keys. Special function because getAllKeys() is broken since memcached 1.4.23.
        * Should only be needed on php 5.6
        *
        * cleaned up version of code found on Stackoverflow.com by Maduka Jayalath
        *
        * @return array|int - all retrieved keys (or negative number on error)
        */
        private function getMemcachedKeys($host = '127.0.0.1', $port = 11211) #NOSONAR
        {
            $mem = @fsockopen($host, $port);
            if ($mem === false) {
                return -1;
            }
            // retrieve distinct slab
            $r = @fwrite($mem, 'stats items' . chr(10));
            if ($r === false) {
                return -2;
            }
            $slab = [];
            while (($l = @fgets($mem, 1024)) !== false) {
                // finished?
                $l = trim($l);
                if ($l == 'END') {
                    break;
                }
                $m = [];
                // <STAT items:22:evicted_nonzero 0>
                $r = preg_match('/^STATsitems:(d+):/', $l, $m);
                if ($r != 1) {
                    return -3;
                }
                $a_slab = $m[1];
                if (!array_key_exists($a_slab, $slab)) {
                    $slab[$a_slab] = [];
                }
            }
            reset($slab);
            foreach ($slab as $a_slab_key => &$a_slab) {
                $r = @fwrite($mem, 'stats cachedump ' . $a_slab_key . ' 100' . chr(10));
                if ($r === false) {
                    return -4;
                }
                while (($l = @fgets($mem, 1024)) !== false) {
                    // finished?
                    $l = trim($l);
                    if ($l == 'END') {
                        break;
                    }
                    $m = [];
                    // ITEM 42 [118 b; 1354717302 s]
                    $r = preg_match('/^ITEMs([^s]+)s/', $l, $m);
                    if ($r != 1) {
                        return -5;
                    }
                    $a_key = $m[1];
                    $a_slab[] = $a_key;
                }
            }
            // close the connection
            @fclose($mem);
            unset($mem);
            $keys = [];
            reset($slab);
            foreach ($slab as &$a_slab) {
                reset($a_slab);
                foreach ($a_slab as &$a_key) {
                    $keys[] = $a_key;
                }
            }
            unset($slab);
            return $keys;
        }
    }
    

    Hope it can help others.


  2. Below just example how to make this kind of migration. Try it that way:

    <?php
    
    $host = '127.0.0.1';
    $port = null;
    $oldPrefix = 'sess_';
    $newPrefix = 'sess_';
    $sessionDir = '/var/lib/php/session/';
    $m = new Redis();
    $m->connect($host, $port);
    // $m = new Memcached();
    // $m->addServer($host, $port);
    $sessions = scandir($sessionDir);
    if (!$sessions) {
        die('nothing to migrate');
    }
    foreach ($sessions as $s) {
        if (in_array($s, ['.', '..'])) {
            continue;
        }
        $sessionName = str_replace($oldPrefix, '', $s);
        $sessionContents = file_get_contents($sessionDir.$s);
        if (!$m->set($newPrefix.$sessionName, $sessionContents)) {
            die(sprintf('Could not migrate session %s'.PHP_EOL, $newPrefix.$sessionName));
        }
        echo '.';
    }
    die(PHP_EOL);
    

    But I guess hat users will be logged out.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search