From 239a022cc01cca52c3f6ddde3231199369cf34c2 Mon Sep 17 00:00:00 2001 From: Alessandro Chitolina Date: Mon, 24 Sep 2018 10:32:31 +0200 Subject: [PATCH] [Cache] add RedisClusterProxy to create lazy connections to Redis clusters --- .../Cache/Traits/RedisClusterProxy.php | 63 +++++++++++++++++++ .../Component/Cache/Traits/RedisTrait.php | 6 +- .../Storage/Handler/RedisSessionHandler.php | 4 +- .../Component/Lock/Store/RedisStore.php | 8 ++- .../Component/Lock/Store/StoreFactory.php | 10 ++- 5 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 src/Symfony/Component/Cache/Traits/RedisClusterProxy.php diff --git a/src/Symfony/Component/Cache/Traits/RedisClusterProxy.php b/src/Symfony/Component/Cache/Traits/RedisClusterProxy.php new file mode 100644 index 0000000000..b4cef59a3e --- /dev/null +++ b/src/Symfony/Component/Cache/Traits/RedisClusterProxy.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +/** + * @author Alessandro Chitolina + * + * @internal + */ +class RedisClusterProxy +{ + private $redis; + private $initializer; + + public function __construct(\Closure $initializer) + { + $this->initializer = $initializer; + } + + public function __call($method, array $args) + { + $this->redis ?: $this->redis = $this->initializer->__invoke(); + + return $this->redis->{$method}(...$args); + } + + public function hscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->redis ?: $this->redis = $this->initializer->__invoke(); + + return $this->redis->hscan($strKey, $iIterator, $strPattern, $iCount); + } + + public function scan(&$iIterator, $strPattern = null, $iCount = null) + { + $this->redis ?: $this->redis = $this->initializer->__invoke(); + + return $this->redis->scan($iIterator, $strPattern, $iCount); + } + + public function sscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->redis ?: $this->redis = $this->initializer->__invoke(); + + return $this->redis->sscan($strKey, $iIterator, $strPattern, $iCount); + } + + public function zscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->redis ?: $this->redis = $this->initializer->__invoke(); + + return $this->redis->zscan($strKey, $iIterator, $strPattern, $iCount); + } +} diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php index 4c5d1564bb..2bedb5bb73 100644 --- a/src/Symfony/Component/Cache/Traits/RedisTrait.php +++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php @@ -52,7 +52,7 @@ trait RedisTrait 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])); } - if (!$redisClient instanceof \Redis && !$redisClient instanceof \RedisArray && !$redisClient instanceof \RedisCluster && !$redisClient instanceof \Predis\Client && !$redisClient instanceof RedisProxy) { + if (!$redisClient instanceof \Redis && !$redisClient instanceof \RedisArray && !$redisClient instanceof \RedisCluster && !$redisClient instanceof \Predis\Client && !$redisClient instanceof RedisProxy && !$redisClient instanceof RedisClusterProxy) { throw new InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\Client, %s given', __METHOD__, \is_object($redisClient) ? \get_class($redisClient) : \gettype($redisClient))); } $this->redis = $redisClient; @@ -237,7 +237,7 @@ trait RedisTrait foreach ($this->redis->_hosts() as $host) { $hosts[] = $this->redis->_instance($host); } - } elseif ($this->redis instanceof \RedisCluster) { + } elseif ($this->redis instanceof RedisClusterProxy || $this->redis instanceof \RedisCluster) { $hosts = array(); foreach ($this->redis->_masters() as $host) { $hosts[] = $h = new \Redis(); @@ -330,7 +330,7 @@ trait RedisTrait { $ids = array(); - if ($this->redis instanceof \RedisCluster || ($this->redis instanceof \Predis\Client && $this->redis->getConnection() instanceof RedisCluster)) { + if ($this->redis instanceof RedisClusterProxy || $this->redis instanceof \RedisCluster || ($this->redis instanceof \Predis\Client && $this->redis->getConnection() instanceof RedisCluster)) { // phpredis & predis don't support pipelining with RedisCluster // see https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#pipelining // see https://github.com/nrk/predis/issues/267#issuecomment-123781423 diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php index 36adf2424d..9c08ddcc01 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php @@ -12,6 +12,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; use Predis\Response\ErrorInterface; +use Symfony\Component\Cache\Traits\RedisClusterProxy; use Symfony\Component\Cache\Traits\RedisProxy; /** @@ -45,7 +46,8 @@ class RedisSessionHandler extends AbstractSessionHandler !$redis instanceof \RedisArray && !$redis instanceof \RedisCluster && !$redis instanceof \Predis\Client && - !$redis instanceof RedisProxy + !$redis instanceof RedisProxy && + !$redis instanceof RedisClusterProxy ) { throw new \InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\Client, %s given', __METHOD__, \is_object($redis) ? \get_class($redis) : \gettype($redis))); } diff --git a/src/Symfony/Component/Lock/Store/RedisStore.php b/src/Symfony/Component/Lock/Store/RedisStore.php index 10174b7f54..08965dbfb1 100644 --- a/src/Symfony/Component/Lock/Store/RedisStore.php +++ b/src/Symfony/Component/Lock/Store/RedisStore.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Lock\Store; +use Symfony\Component\Cache\Traits\RedisClusterProxy; use Symfony\Component\Cache\Traits\RedisProxy; use Symfony\Component\Lock\Exception\InvalidArgumentException; use Symfony\Component\Lock\Exception\LockConflictedException; @@ -130,7 +131,12 @@ class RedisStore implements StoreInterface */ private function evaluate(string $script, string $resource, array $args) { - if ($this->redis instanceof \Redis || $this->redis instanceof \RedisCluster || $this->redis instanceof RedisProxy) { + if ( + $this->redis instanceof \Redis || + $this->redis instanceof \RedisCluster || + $this->redis instanceof RedisProxy || + $this->redis instanceof RedisClusterProxy + ) { return $this->redis->eval($script, array_merge(array($resource), $args), 1); } diff --git a/src/Symfony/Component/Lock/Store/StoreFactory.php b/src/Symfony/Component/Lock/Store/StoreFactory.php index 701bf2b8bb..5f97a8715d 100644 --- a/src/Symfony/Component/Lock/Store/StoreFactory.php +++ b/src/Symfony/Component/Lock/Store/StoreFactory.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Lock\Store; +use Symfony\Component\Cache\Traits\RedisClusterProxy; use Symfony\Component\Cache\Traits\RedisProxy; use Symfony\Component\Lock\Exception\InvalidArgumentException; @@ -28,7 +29,14 @@ class StoreFactory */ public static function createStore($connection) { - if ($connection instanceof \Redis || $connection instanceof \RedisArray || $connection instanceof \RedisCluster || $connection instanceof \Predis\Client || $connection instanceof RedisProxy) { + if ( + $connection instanceof \Redis || + $connection instanceof \RedisArray || + $connection instanceof \RedisCluster || + $connection instanceof \Predis\Client || + $connection instanceof RedisProxy || + $connection instanceof RedisClusterProxy + ) { return new RedisStore($connection); } if ($connection instanceof \Memcached) {