Skip to content

Commit 814ffab

Browse files
renannicolas-grekas
authored andcommitted
[Cache] Support Redis Sentinel mode when using phpredis/phpredis extension
1 parent 9d40bd8 commit 814ffab

File tree

4 files changed

+62
-10
lines changed

4 files changed

+62
-10
lines changed

src/Symfony/Component/Cache/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
5.3.0
5+
-----
6+
7+
* added support for connecting to Redis Sentinel clusters when using the Redis PHP extension
8+
49
5.2.0
510
-----
611

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Cache\Tests\Adapter;
13+
14+
use Symfony\Component\Cache\Adapter\AbstractAdapter;
15+
16+
/**
17+
* @group integration
18+
*/
19+
class PredisAdapterSentinelTest extends AbstractRedisAdapterTest
20+
{
21+
public static function setUpBeforeClass(): void
22+
{
23+
if (!class_exists(\Predis\Client::class)) {
24+
self::markTestSkipped('The Predis\Client class is required.');
25+
}
26+
if (!$hosts = getenv('REDIS_SENTINEL_HOSTS')) {
27+
self::markTestSkipped('REDIS_SENTINEL_HOSTS env var is not defined.');
28+
}
29+
if (!$service = getenv('REDIS_SENTINEL_SERVICE')) {
30+
self::markTestSkipped('REDIS_SENTINEL_SERVICE env var is not defined.');
31+
}
32+
33+
self::$redis = AbstractAdapter::createConnection('redis:?host['.str_replace(' ', ']&host[', $hosts).']', ['redis_sentinel' => $service, 'class' => \Predis\Client::class]);
34+
}
35+
}

src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterSentinelTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ class RedisAdapterSentinelTest extends AbstractRedisAdapterTest
2121
{
2222
public static function setUpBeforeClass(): void
2323
{
24-
if (!class_exists('Predis\Client')) {
25-
self::markTestSkipped('The Predis\Client class is required.');
24+
if (!class_exists(\RedisSentinel::class)) {
25+
self::markTestSkipped('The RedisSentinel class is required.');
2626
}
2727
if (!$hosts = getenv('REDIS_SENTINEL_HOSTS')) {
2828
self::markTestSkipped('REDIS_SENTINEL_HOSTS env var is not defined.');

src/Symfony/Component/Cache/Traits/RedisTrait.php

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,17 @@ public static function createConnection($dsn, array $options = [])
159159
throw new InvalidArgumentException(sprintf('Invalid Redis DSN: "%s".', $dsn));
160160
}
161161

162-
if (isset($params['redis_sentinel']) && !class_exists(\Predis\Client::class)) {
163-
throw new CacheException(sprintf('Redis Sentinel support requires the "predis/predis" package: "%s".', $dsn));
162+
$params += $query + $options + self::$defaultConnectionOptions;
163+
164+
if (isset($params['redis_sentinel']) && (!class_exists(\Predis\Client::class) || !class_exists(\RedisSentinel::class))) {
165+
throw new CacheException(sprintf('Redis Sentinel support requires the "predis/predis" package or the "redis" extension v5.2 or higher: "%s".', $dsn));
164166
}
165167

166-
$params += $query + $options + self::$defaultConnectionOptions;
168+
if ($params['redis_cluster'] && isset($params['redis_sentinel'])) {
169+
throw new InvalidArgumentException(sprintf('Cannot use both "redis_cluster" and "redis_sentinel" at the same time: "%s".', $dsn));
170+
}
167171

168-
if (null === $params['class'] && !isset($params['redis_sentinel']) && \extension_loaded('redis')) {
172+
if (null === $params['class'] && \extension_loaded('redis')) {
169173
$class = $params['redis_cluster'] ? \RedisCluster::class : (1 < \count($hosts) ? \RedisArray::class : \Redis::class);
170174
} else {
171175
$class = null === $params['class'] ? \Predis\Client::class : $params['class'];
@@ -176,8 +180,19 @@ public static function createConnection($dsn, array $options = [])
176180
$redis = new $class();
177181

178182
$initializer = static function ($redis) use ($connect, $params, $dsn, $auth, $hosts) {
183+
$host = $hosts[0]['host'] ?? $hosts[0]['path'];
184+
$port = $hosts[0]['port'] ?? null;
185+
186+
if (isset($params['redis_sentinel'])) {
187+
$sentinel = new \RedisSentinel($host, $port, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval']);
188+
189+
if (![$host, $port] = $sentinel->getMasterAddrByName($params['redis_sentinel'])) {
190+
throw new InvalidArgumentException(sprintf('Failed to retrieve master information from master name "%s" and address "%s:%d".', $params['redis_sentinel'], $host, $port));
191+
}
192+
}
193+
179194
try {
180-
@$redis->{$connect}($hosts[0]['host'] ?? $hosts[0]['path'], $hosts[0]['port'] ?? null, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval']);
195+
@$redis->{$connect}($host, $port, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval']);
181196

182197
set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
183198
$isConnected = $redis->isConnected();
@@ -254,9 +269,6 @@ public static function createConnection($dsn, array $options = [])
254269
} elseif (is_a($class, \Predis\ClientInterface::class, true)) {
255270
if ($params['redis_cluster']) {
256271
$params['cluster'] = 'redis';
257-
if (isset($params['redis_sentinel'])) {
258-
throw new InvalidArgumentException(sprintf('Cannot use both "redis_cluster" and "redis_sentinel" at the same time: "%s".', $dsn));
259-
}
260272
} elseif (isset($params['redis_sentinel'])) {
261273
$params['replication'] = 'sentinel';
262274
$params['service'] = $params['redis_sentinel'];

0 commit comments

Comments
 (0)
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