diff --git a/composer.json b/composer.json index f37dfd5c..b95a6bea 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "doctrine/coding-standard": "^10.0", "friendsofphp/proxy-manager-lts": "^1.0.6", "monolog/monolog": "*", - "phpunit/phpunit": "^8.5 || ^9.5", + "phpunit/phpunit": "^8.5.32 || ^9.5.28", "predis/predis": "^2.0", "symfony/browser-kit": "^4.4 || ^5.3 || ^6.0", "symfony/cache": "^4.4 || ^5.3 || ^6.0", diff --git a/src/Factory/PhpredisClientFactory.php b/src/Factory/PhpredisClientFactory.php index a17e8f5f..e928be9d 100644 --- a/src/Factory/PhpredisClientFactory.php +++ b/src/Factory/PhpredisClientFactory.php @@ -11,9 +11,11 @@ use ProxyManager\Proxy\AccessInterceptorInterface; use Redis; use RedisCluster; +use RedisException; use RedisSentinel; use ReflectionClass; use ReflectionMethod; +use Relay\Exception as RelayException; use Relay\Relay; use Relay\Sentinel; use Snc\RedisBundle\DependencyInjection\Configuration\RedisDsn; @@ -102,19 +104,34 @@ public function create(string $class, array $dsns, array $options, string $alias } /** - * @param class-string $class - * @param list $dsns - * @param array{service: ?string} $options + * @param class-string $class + * @param list $dsns + * @param array{service: ?string, connection_persistent: ?bool, connection_timeout: ?string, read_write_timeout: ?string} $options * * @return Redis|Relay */ private function createClientFromSentinel(string $class, array $dsns, string $alias, array $options, bool $loggingEnabled) { - $isRelay = is_a($class, Sentinel::class, true); - $sentinelClass = $isRelay ? Sentinel::class : RedisSentinel::class; + $isRelay = is_a($class, Sentinel::class, true); + $sentinelClass = $isRelay ? Sentinel::class : RedisSentinel::class; + $masterName = $options['service']; + $connectionTimeout = $options['connection_timeout'] ?? 0; + $connectionPersistent = $options['connection_persistent'] ? $masterName : null; + $readTimeout = $options['read_write_timeout'] ?? 0; foreach ($dsns as $dsn) { - $address = (new $sentinelClass($dsn->getHost(), (int) $dsn->getPort()))->getMasterAddrByName($options['service']); + try { + $address = (new $sentinelClass( + $dsn->getHost(), + (int) $dsn->getPort(), + $connectionTimeout, + $connectionPersistent, + 5, // retry interval + $readTimeout, + ))->getMasterAddrByName($masterName); + } catch (RedisException | RelayException $e) { + continue; + } if (!$address) { continue; @@ -139,7 +156,7 @@ public function __construct(string $dsn, string $host, int $port) throw new InvalidArgumentException( sprintf( 'Failed to retrieve master information from sentinel %s and dsn %s.', - var_export($options['service'], true), + var_export($masterName, true), var_export($dsns, true), ), ); diff --git a/tests/Factory/PhpredisClientFactoryTest.php b/tests/Factory/PhpredisClientFactoryTest.php index 8f90e1fd..aacf377c 100644 --- a/tests/Factory/PhpredisClientFactoryTest.php +++ b/tests/Factory/PhpredisClientFactoryTest.php @@ -115,8 +115,15 @@ public function testCreateSentinelConfig(string $sentinelClass, string $outputCl $client = $factory->create( $sentinelClass, - ['redis://sncredis@localhost:26379'], - ['connection_timeout' => 5, 'connection_persistent' => false, 'service' => 'mymaster'], + [ + 'redis://undefined@localhost:55555', // unreachable instance + 'redis://sncredis@localhost:26379', + ], + [ + 'connection_timeout' => 5, + 'connection_persistent' => false, + 'service' => 'mymaster', + ], 'phpredissentinel', true, ); @@ -124,6 +131,7 @@ public function testCreateSentinelConfig(string $sentinelClass, string $outputCl $this->assertInstanceOf($outputClass, $client); $this->assertNull($client->getOption(Redis::OPT_PREFIX)); $this->assertSame(0, $client->getOption(Redis::OPT_SERIALIZER)); + $this->assertSame(5., $client->getTimeout()); $this->assertSame('sncredis', $client->getAuth()); }