feature #31437 [Cache] Add Redis Sentinel support (StephenClouse)
This PR was squashed before being merged into the 4.4-dev branch (closes #31437).
Discussion
----------
[Cache] Add Redis Sentinel support
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets |
| License | MIT
| Doc PR | symfony/symfony-docs#11545
This change adds support for Redis Sentinel clusters to the Cache component Redis adapter.
The DSN format is syntactically equivalent to cluster support, but adds a new parameter `redis_sentinel` that should be set to the sentinel service name.
This support requires the use of predis as the underlying connection library. The redis extension does not support sentinel at this time.
Commits
-------
80e8b21525
[Cache] Add Redis Sentinel support
This commit is contained in:
commit
63d730920b
@ -1,6 +1,11 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
4.4.0
|
||||
-----
|
||||
|
||||
* added support for connecting to Redis Sentinel clusters
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
||||
|
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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 Symfony\Component\Cache\Adapter\AbstractAdapter;
|
||||
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||||
|
||||
class RedisAdapterSentinelTest extends AbstractRedisAdapterTest
|
||||
{
|
||||
public static function setupBeforeClass()
|
||||
{
|
||||
if (!class_exists('Predis\Client')) {
|
||||
self::markTestSkipped('The Predis\Client class is required.');
|
||||
}
|
||||
if (!$hosts = getenv('REDIS_SENTINEL_HOSTS')) {
|
||||
self::markTestSkipped('REDIS_SENTINEL_HOSTS env var is not defined.');
|
||||
}
|
||||
if (!$service = getenv('REDIS_SENTINEL_SERVICE')) {
|
||||
self::markTestSkipped('REDIS_SENTINEL_SERVICE env var is not defined.');
|
||||
}
|
||||
|
||||
self::$redis = AbstractAdapter::createConnection('redis:?host['.str_replace(' ', ']&host[', $hosts).']', ['redis_sentinel' => $service]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Invalid Redis DSN: cannot use both redis_cluster and redis_sentinel at the same time
|
||||
*/
|
||||
public function testInvalidDSNHasBothClusterAndSentinel()
|
||||
{
|
||||
$dsn = 'redis:?host[redis1]&host[redis2]&host[redis3]&redis_cluster=1&redis_sentinel=mymaster';
|
||||
RedisAdapter::createConnection($dsn);
|
||||
}
|
||||
}
|
@ -38,6 +38,7 @@ trait RedisTrait
|
||||
'tcp_keepalive' => 0,
|
||||
'lazy' => null,
|
||||
'redis_cluster' => false,
|
||||
'redis_sentinel' => null,
|
||||
'dbindex' => 0,
|
||||
'failover' => 'none',
|
||||
];
|
||||
@ -146,9 +147,13 @@ trait RedisTrait
|
||||
throw new InvalidArgumentException(sprintf('Invalid Redis DSN: %s', $dsn));
|
||||
}
|
||||
|
||||
if (isset($params['redis_sentinel']) && !class_exists(\Predis\Client::class)) {
|
||||
throw new CacheException(sprintf('Redis Sentinel support requires the "predis/predis" package: %s', $dsn));
|
||||
}
|
||||
|
||||
$params += $query + $options + self::$defaultConnectionOptions;
|
||||
|
||||
if (null === $params['class'] && \extension_loaded('redis')) {
|
||||
if (null === $params['class'] && !isset($params['redis_sentinel']) && \extension_loaded('redis')) {
|
||||
$class = $params['redis_cluster'] ? \RedisCluster::class : (1 < \count($hosts) ? \RedisArray::class : \Redis::class);
|
||||
} else {
|
||||
$class = null === $params['class'] ? \Predis\Client::class : $params['class'];
|
||||
@ -246,6 +251,12 @@ trait RedisTrait
|
||||
} elseif (is_a($class, \Predis\Client::class, true)) {
|
||||
if ($params['redis_cluster']) {
|
||||
$params['cluster'] = 'redis';
|
||||
if (isset($params['redis_sentinel'])) {
|
||||
throw new InvalidArgumentException(sprintf('Cannot use both "redis_cluster" and "redis_sentinel" at the same time: %s', $dsn));
|
||||
}
|
||||
} elseif (isset($params['redis_sentinel'])) {
|
||||
$params['replication'] = 'sentinel';
|
||||
$params['service'] = $params['redis_sentinel'];
|
||||
}
|
||||
$params += ['parameters' => []];
|
||||
$params['parameters'] += [
|
||||
@ -268,6 +279,9 @@ trait RedisTrait
|
||||
}
|
||||
|
||||
$redis = new $class($hosts, array_diff_key($params, self::$defaultConnectionOptions));
|
||||
if (isset($params['redis_sentinel'])) {
|
||||
$redis->getConnection()->setSentinelTimeout($params['timeout']);
|
||||
}
|
||||
} elseif (class_exists($class, false)) {
|
||||
throw new InvalidArgumentException(sprintf('"%s" is not a subclass of "Redis", "RedisArray", "RedisCluster" nor "Predis\Client".', $class));
|
||||
} else {
|
||||
|
Reference in New Issue
Block a user