8000 [Messenger] [Redis] Redis Sentinel support- issue #41762 · symfony/symfony@9aabcfa · GitHub
[go: up one dir, main page]

Skip to content

Commit 9aabcfa

Browse files
[Messenger] [Redis] Redis Sentinel support- issue #41762
1 parent b3e1322 commit 9aabcfa

File tree

4 files changed

+74
-3
lines changed

4 files changed

+74
-3
lines changed

src/Symfony/Component/Messenger/Bridge/Redis/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ CHANGELOG
88
* Deprecate TLS option, use `rediss://127.0.0.1` instead of `redis://127.0.0.1?tls=1`
99
* Add support for `\RedisCluster` instance in `Connection` constructor
1010
* Add support for Redis Cluster in DSN
11+
* Add support for Redis Sentinel
1112

1213
5.2.0
1314
-----

src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/ConnectionTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,4 +377,36 @@ public function testLastErrorGetsCleared()
377377

378378
$this->assertSame('xack error', $e->getMessage());
379379
}
380+
381+
public function testSentinelEnabledWithRedisInstance()
382+
{
383+
$this->expectException(\InvalidArgumentException::class);
384+
8000 $this->expectExceptionMessage('Cannot configure Redis Sentinel and Redis instance at the same time');
385+
386+
$redis = $this->createMock(\Redis::class);
387+
Connection::fromDsn('redis://localhost/messenger-clearlasterror', ['sentinel_master' => 'any'], $redis);
388+
}
389+
390+
public function testSentinelEnabledWithRedisClusterInstance()
391+
{
392+
$this->expectException(\InvalidArgumentException::class);
393+
$this->expectExceptionMessage('Cannot configure Redis Sentinel and Redis Cluster instance at the same time');
394+
395+
$redis = $this->createMock(\RedisCluster::class);
396+
Connection::fromDsn('redis://localhost/messenger-clearlasterror', ['sentinel_master' => 'any'], $redis);
397+
}
398+
399+
public function testInvalidSentinelMasterName()
400+
{
401+
$master = getenv('MESSENGER_REDIS_SENTINEL_MASTER');
402+
if (!(bool) $master) {
403+
self::markTestSkipped('skip because redis sentinel is not configured');
404+
}
405+
406+
$uid = uniqid('sentinel_');
407+
$this->expectException(\InvalidArgumentException::class);
408+
$this->expectExceptionMessage(sprintf('Failed to retrieve master information from master name "%s" and address "127.0.0.1:5000"', $uid));
409+
410+
Connection::fromDsn('redis://127.0.0.1:5000/messenger-clearlasterror', ['sentinel_master' => $uid], null);
411+
}
380412
}

src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/RedisExtIntegrationTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ protected function setUp(): void
3434

3535
try {
3636
$this->redis = new \Redis();
37-
$this->connection = Connection::fromDsn(getenv('MESSENGER_REDIS_DSN'), [], $this->redis);
37+
$this->connection = Connection::fromDsn(getenv('MESSENGER_REDIS_DSN'), ['sentinel_master' => getenv('MESSENGER_REDIS_SENTINEL_MASTER')], $this->redis);
3838
$this->connection->cleanup();
3939
$this->connection->setup();
4040
} catch (\Exception $e) {
@@ -110,7 +110,7 @@ public function testConnectionSendDelayedMessagesWithSameContent()
110110
public function testConnectionBelowRedeliverTimeout()
111111
{
112112
// lower redeliver timeout and claim interval
113-
$connection = Connection::fromDsn(getenv('MESSENGER_REDIS_DSN'), [], $this->redis);
113+
$connection = Connection::fromDsn(getenv('MESSENGER_REDIS_DSN'), ['sentinel_master' => getenv('MESSENGER_REDIS_SENTINEL_MASTER')], $this->redis);
114114

115115
$connection->cleanup();
116116
$connection->setup();
@@ -138,7 +138,7 @@ public function testConnectionClaimAndRedeliver()
138138
// lower redeliver timeout and claim interval
139139
$connection = Connection::fromDsn(
140140
getenv('MESSENGER_REDIS_DSN'),
141-
['redeliver_timeout' => 0, 'claim_interval' => 500],
141+
['redeliver_timeout' => 0, 'claim_interval' => 500, 'sentinel_master' => getenv('MESSENGER_REDIS_SENTINEL_MASTER')],
142142
$this->redis
143143
);
144144

src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ class Connection
4242
'lazy' => false,
4343
'auth' => null,
4444
'serializer' => \Redis::SERIALIZER_PHP,
45+
'sentinel_master' => null,
46+
'sentinel_timeout' => 1000, // ms
47+
'sentinel_read_timeout' => 1000, // ms
48+
'sentinel_retry_interval' => 1000, // ms
49+
'sentinel_persistent_id' => null,
4550
];
4651

4752
private $connection;
@@ -57,6 +62,11 @@ class Connection
5762
private $deleteAfterAck;
5863
private $deleteAfterReject;
5964
private $couldHavePendingMessages = true;
65+
private $sentinelMaster;
66+
private $sentinelTimeout;
67+
private $sentinelReadTimeout;
68+
private $sentinelRetryInterval;
69+
private $sentinelPersistentId;
6070

6171
/**
6272
* @param \Redis|\RedisCluster|null $redis
@@ -76,6 +86,24 @@ public function __construct(array $configuration, array $connectionCredentials =
7686
$auth = null;
7787
}
7888

89+
$this->sentinelMaster = $redisOptions['sentinel_master'] ?? null;
90+
$this->sentinelTimeout = $redisOptions['sentinel_timeout'] ?? self::DEFAULT_OPTIONS['sentinel_timeout'];
91+
$this->sentinelReadTimeout = $redisOptions['sentinel_read_timeout'] ?? self::DEFAULT_OPTIONS['sentinel_read_timeout'];
92+
$this->sentinelRetryInterval = $redisOptions['sentinel_retry_interval'] ?? self::DEFAULT_OPTIONS['sentinel_retry_interval'];
93+
$this->sentinelPersistentId = $redisOptions['sentinel_persistent_id'] ?? self::DEFAULT_OPTIONS['sentinel_persistent_id'];
94+
95+
if (null !== $this->sentinelMaster && !class_exists(\Predis\Client::class) && !class_exists(\RedisSentinel::class)) {
96+
throw new InvalidArgumentException('Redis Sentinel support requires the "predis/predis" package or the "redis" extension v5.2 or higher.');
97+
}
98+
99+
if (null !== $this->sentinelMaster && $redis instanceof \RedisCluster) {
100+
throw new InvalidArgumentException('Cannot configure Redis Sentinel and Redis Cluster instance at the same time.');
101+
}
102+
103+
if (null !== $this->sentinelMaster && $redis instanceof \Redis) {
104+
throw new InvalidArgumentException('Cannot configure Redis Sentinel and Redis instance at the same time.');
105+
}
106+
79107
$lazy = $configuration['lazy'] ?? self::DEFAULT_OPTIONS['lazy'];
80108
if (\is_array($host) || $redis instanceof \RedisCluster) {
81109
$hosts = \is_string($host) ? [$host.':'.$port] : $host; // Always ensure we have an array
@@ -84,6 +112,16 @@ public function __construct(array $configuration, array $connectionCredentials =
84112
};
85113
$redis = $lazy ? new RedisClusterProxy($redis, $initializer) : $initializer($redis);
86114
} else {
115+
if (null !== $this->sentinelMaster) {
116+
$sentinelClient = new \RedisSentinel($host, $port, $this->sentinelTimeout, $this->sentinelPersistentId, $this->sentinelRetryInterval, $this->sentinelReadTimeout);
117+
118+
if (!$address = $sentinelClient->getMasterAddrByName($this->sentinelMaster)) {
119+
throw new InvalidArgumentException(sprintf('Failed to retrieve master information from master name "%s" and address "%s:%d".', $this->sentinelMaster, $host, $port));
120+
}
121+
122+
[$host, $port] = $address;
123+
}
124+
87125
$redis = $redis ?? new \Redis();
88126
$initializer = static function ($redis) use ($host, $port, $auth, $serializer, $dbIndex) {
89127
return self::initializeRedis($redis, $host, $port, $auth, $serializer, $dbIndex);

0 commit comments

Comments
 (0)
0