From 4893cbc24a863b322675abc2a71852f238c5057f Mon Sep 17 00:00:00 2001 From: Aurimas Niekis Date: Tue, 19 Jan 2016 14:27:46 +0100 Subject: [PATCH 1/2] Added RedisAdapter --- .travis.yml | 4 +- .../Component/Cache/Adapter/RedisAdapter.php | 107 ++++++++++++++++++ .../Cache/Tests/Adapter/RedisAdapterTest.php | 47 ++++++++ 3 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Component/Cache/Adapter/RedisAdapter.php create mode 100644 src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php diff --git a/.travis.yml b/.travis.yml index 101f35a98ab6c..46a71e9cc8f30 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,9 @@ cache: - .phpunit - php-$MIN_PHP -services: mongodb +services: + - mongodb + - redis-server before_install: # Matrix lines for intermediate PHP versions are skipped for pull requests diff --git a/src/Symfony/Component/Cache/Adapter/RedisAdapter.php b/src/Symfony/Component/Cache/Adapter/RedisAdapter.php new file mode 100644 index 0000000000000..122a4d313dd73 --- /dev/null +++ b/src/Symfony/Component/Cache/Adapter/RedisAdapter.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +/** + * @author Aurimas Niekis + */ +class RedisAdapter extends AbstractAdapter +{ + /** + * @var \Redis + */ + private $redis; + + /** + * @param \Redis $redisConnection + * @param string $namespace + * @param int $defaultLifetime + */ + public function __construct(\Redis $redisConnection, $namespace = '', $defaultLifetime = 0) + { + $this->redis = $redisConnection; + + parent::__construct($namespace, $defaultLifetime); + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + $values = $this->redis->mget($ids); + + $index = 0; + $result = []; + + foreach ($ids as $id) { + $value = $values[$index++]; + + if (false === $value) { + continue; + } + + $result[$id] = unserialize($value); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + return $this->redis->exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doClear() + { + return $this->redis->flushDB(); + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + $this->redis->del($ids); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + $failed = []; + foreach ($values as $key => $value) { + $value = serialize($value); + + if ($lifetime < 1) { + $response = $this->redis->set($key, $value); + } else { + $response = $this->redis->setex($key, $lifetime, $value); + } + + if (false === $response) { + $failed[] = $key; + } + } + + return count($failed) > 0 ? $failed : true; + } +} diff --git a/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php new file mode 100644 index 0000000000000..564ce01d19b15 --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Cache\IntegrationTests\CachePoolTest; +use Symfony\Component\Cache\Adapter\RedisAdapter; + +class RedisAdapterTest extends CachePoolTest +{ + /** + * @var \Redis + */ + private static $redis; + + public function createCachePool() + { + return new RedisAdapter($this->getRedis(), __CLASS__); + } + + private function getRedis() + { + if (self::$redis) { + return self::$redis; + } + + self::$redis = new \Redis(); + self::$redis->connect('127.0.0.1'); + self::$redis->select(1993); + + return self::$redis; + } + + public static function tearDownAfterClass() + { + self::$redis->flushDB(); + self::$redis->close(); + } +} From 6b7a1fcefadb4a343cb3551b93ce8b1d9e236df0 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 15 Mar 2016 11:06:34 +0100 Subject: [PATCH 2/2] [Cache] Finish Redis adapter --- .travis.yml | 1 + .../Component/Cache/Adapter/RedisAdapter.php | 64 +++++++++++-------- .../Cache/Tests/Adapter/RedisAdapterTest.php | 20 +++--- 3 files changed, 46 insertions(+), 39 deletions(-) diff --git a/.travis.yml b/.travis.yml index 46a71e9cc8f30..45edcbdc193e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,6 +50,7 @@ before_install: - if [[ $TRAVIS_PHP_VERSION = 5.* && ! $deps ]]; then (cd src/Symfony/Component/Debug/Resources/ext && phpize && ./configure && make && echo extension = $(pwd)/modules/symfony_debug.so >> $INI_FILE); fi; - if [[ $TRAVIS_PHP_VERSION = 5.* ]]; then pecl install -f memcached-2.1.0; fi; - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then echo extension = ldap.so >> $INI_FILE; fi; + - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then echo extension = redis.so >> $INI_FILE; fi; - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then phpenv config-rm xdebug.ini; fi; - if [[ $deps != skip ]]; then composer self-update; fi; - if [[ $deps != skip && $TRAVIS_REPO_SLUG = symfony/symfony ]]; then cp .composer/* ~/.composer/; composer global install --prefer-dist; fi; diff --git a/src/Symfony/Component/Cache/Adapter/RedisAdapter.php b/src/Symfony/Component/Cache/Adapter/RedisAdapter.php index 122a4d313dd73..4b7586868291e 100644 --- a/src/Symfony/Component/Cache/Adapter/RedisAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/RedisAdapter.php @@ -11,25 +11,23 @@ namespace Symfony\Component\Cache\Adapter; +use Symfony\Component\Cache\Exception\InvalidArgumentException; + /** * @author Aurimas Niekis */ class RedisAdapter extends AbstractAdapter { - /** - * @var \Redis - */ private $redis; - /** - * @param \Redis $redisConnection - * @param string $namespace - * @param int $defaultLifetime - */ public function __construct(\Redis $redisConnection, $namespace = '', $defaultLifetime = 0) { $this->redis = $redisConnection; + if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) { + throw new InvalidArgumentException(sprintf('RedisAdapter namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0])); + } + parent::__construct($namespace, $defaultLifetime); } @@ -39,18 +37,13 @@ public function __construct(\Redis $redisConnection, $namespace = '', $defaultLi protected function doFetch(array $ids) { $values = $this->redis->mget($ids); - $index = 0; $result = []; foreach ($ids as $id) { - $value = $values[$index++]; - - if (false === $value) { - continue; + if (false !== $value = $values[$index++]) { + $result[$id] = unserialize($value); } - - $result[$id] = unserialize($value); } return $result; @@ -67,9 +60,19 @@ protected function doHave($id) /** * {@inheritdoc} */ - protected function doClear() + protected function doClear($namespace) { - return $this->redis->flushDB(); + if (!isset($namespace[0])) { + $this->redis->flushDB(); + } else { + // As documented in Redis documentation (http://redis.io/commands/keys) using KEYS + // can hang your server when it is executed against large databases (millions of items). + // Whenever you hit this scale, it is advised to deploy one Redis database per cache pool + // instead of using namespaces, so that the above FLUSHDB is used instead. + $this->redis->eval(sprintf("local keys=redis.call('KEYS','%s*') for i=1,#keys,5000 do redis.call('DEL',unpack(keys,i,math.min(i+4999,#keys))) end", $namespace)); + } + + return true; } /** @@ -87,21 +90,26 @@ protected function doDelete(array $ids) */ protected function doSave(array $values, $lifetime) { - $failed = []; - foreach ($values as $key => $value) { - $value = serialize($value); - - if ($lifetime < 1) { - $response = $this->redis->set($key, $value); - } else { - $response = $this->redis->setex($key, $lifetime, $value); + $failed = array(); + + foreach ($values as $id => $v) { + try { + $values[$id] = serialize($v); + } catch (\Exception $e) { + $failed[] = $id; } + } + + if (!$this->redis->mSet($values)) { + return false; + } - if (false === $response) { - $failed[] = $key; + if ($lifetime >= 1) { + foreach ($values as $id => $v) { + $this->redis->expire($id, $lifetime); } } - return count($failed) > 0 ? $failed : true; + return $failed; } } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php index 564ce01d19b15..16428929e2cc8 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php @@ -14,29 +14,27 @@ use Cache\IntegrationTests\CachePoolTest; use Symfony\Component\Cache\Adapter\RedisAdapter; +/** + * @requires extension redis + */ class RedisAdapterTest extends CachePoolTest { - /** - * @var \Redis - */ private static $redis; public function createCachePool() { - return new RedisAdapter($this->getRedis(), __CLASS__); + if (defined('HHVM_VERSION')) { + $this->skippedTests['testDeferredSaveWithoutCommit'] = 'Fails on HHVM'; + } + + return new RedisAdapter(self::$redis, str_replace('\\', '.', __CLASS__)); } - private function getRedis() + public static function setupBeforeClass() { - if (self::$redis) { - return self::$redis; - } - self::$redis = new \Redis(); self::$redis->connect('127.0.0.1'); self::$redis->select(1993); - - return self::$redis; } public static function tearDownAfterClass() pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy