Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,37 @@ The important parts are the `tls://` protocol in `sentinel_host` as well as the

Because Redis Sentinel resolves Redis instances by IP and port, your Redis certificate needs to have the IP as SAN. Alternatively, you can set `verify_peer` and maybe also `verify_peer_name` to `false`.

### Multiple Sentinel instances

If your environment is composed of multiple Redis Sentinel instances, you can use the `sentinel_hosts` configuration instead of `sentinel_host` and `sentinel_port`.
`sentinel_hosts` is an array where each entry contains a `host` and a `port` key corresponding to a Sentinel instance.

#### Example configuration using multiple Redis Sentinel instances

```php
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis-sentinel'),

'default' => [
'sentinel_hosts' => [
['host' => '127.0.0.1', 'port' => 26379],
['host' => '127.0.0.1', 'port' => 26380],
['host' => '127.0.0.1', 'port' => 26381],
],
'sentinel_service' => 'mymaster',
'sentinel_timeout' => 0,
'sentinel_persistent' => false,
'sentinel_retry_interval' => 0,
'sentinel_read_timeout' => 0,
'sentinel_username' => 'sentinel_username',
'sentinel_password' => 'sentinel_password',

'password' => 'password'
'database' => 1,
]
]
```

### How does it work?

An additional Laravel Redis driver is added (`phpredis-sentinel`), which resolves the currently declared master instance of a replication
Expand Down
41 changes: 35 additions & 6 deletions src/Connectors/PhpRedisSentinelConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,10 @@ public function connect(array $config, array $options): PhpRedisSentinelConnecti
*/
protected function createClient(array $config): Redis
{
$service = $config['sentinel_service'] ?? 'mymaster';

$sentinel = $this->connectToSentinel($config);

$master = $sentinel->master($service);
$master = $this->getMaster($config);

if (! $this->isValidMaster($master)) {
throw new RedisException(sprintf("No master found for service '%s'.", $service));
throw new RedisException(sprintf("No master found for service '%s'.", $config['sentinel_service'] ?? 'mymaster'));
}

return parent::createClient(array_merge($config, [
Expand All @@ -102,6 +98,39 @@ protected function isValidMaster(mixed $master): bool
return is_array($master) && isset($master['ip']) && isset($master['port']);
}

/**
* Get the master for the given service.
*
* @throws ConfigurationException
* @throws RedisException
*/
private function getMaster(array $config): array|false
{
$service = $config['sentinel_service'] ?? 'mymaster';

$exception = null;
$hosts = $config['sentinel_hosts'] ?? [];

foreach ($hosts as $host) {
$hostConfig = array_merge($config, [
'sentinel_host' => $host['host'] ?? null,
'sentinel_port' => ((int) $host['port']) ?? null,
]);

try {
return $this->connectToSentinel($hostConfig)->master($service);
} catch (RedisException $e) {
$exception = $e;
}
}

if ($exception !== null) {
throw $exception;
}

return $this->connectToSentinel($config)->master($service);
}

/**
* Connect to the configured Redis Sentinel instance.
*
Expand Down