Laravel + predis + Redis cluster - MOVED / no connection to 127.0.0.1:6379
TL;DR:
'cluster' => true
should be true to create one aggregate client that handles multiple nodes.'options' => ['cluster' => 'redis']
needs to be added to the configuration as a sibling ofdefault
(not a child) in order to tell Predis to handle the server-side clustering provided by Azure.- if using auth with server-side clustering,
'options' => [ 'cluster' => 'redis', 'parameters' => ['password' => env('REDIS_PASSWORD', null)], ]
will be needed to auth newly discovered cluster nodes.
Full Text
In the redis configuration, you can set up multiple connections to multiple redis instances. The cluster
option tells Laravel how to handle those multiple defined connections.
If cluster
is set to false
, Laravel will create individual \Predis\Client
instances for each connection. Each connection can be accessed individually, and will not have any relation to another connection.
If cluster
is set to true
, Laravel will create an aggregate \Predis\Client
instance using all the defined connections. With no other configuration, this is kind of a "fake" cluster. It uses client-side sharding to distribute the keyspace and may require external monitoring and maintenance to ensure a proper key load balance.
The issue you're running into, however, is that Azure implements (presumably) a real server-side Redis cluster, which handles automatic sharding of the keyspace. In this instance, the nodes know about each other and talk to each other, and may go up and down. This is where MOVED
and ASK
responses come from.
The Predis
library can automatically handle these responses, but only when you tell it that it needs to. In this case, you need to tell the Predis
client that it needs to handle clustering, and this is done by Laravel through the options
array on the redis
configuration.
On the redis
configuration, the options
key should be a sibling of your connections (i.e. default
), not a child. Additionally, the options should be specified as key => value
pairs.
So, your configuration should look like:
'redis' => [
'cluster' => true,
'default' => [
'host' => env('REDIS_HOST', 'localhost'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
],
'options' => [
'cluster' => 'redis',
],
],
The cluster
key under the redis
config will tell Laravel to create an aggregate Predis\Client
instance that may handle multiple nodes, and the cluster
key under the options
array will tell that instance that it needs to handle server-side clustering, not client-side clustering.
Auth
The original connection parameters (including authentication) are not shared with connections to new nodes discovered via -MOVED
and -ASK
responses. So, any errors you previously got from -MOVED
responses will now just convert to NOAUTH
errors. However, the server-side 'cluster'
configuration allows for a 'parameters'
sibling which defines a list of parameters to use with newly discovered nodes. This is where you can put your auth parameters to use with new nodes.
I believe this will look something like:
'redis' => [
'cluster' => true,
'default' => [
'host' => env('REDIS_HOST', 'localhost'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
],
'options' => [
'cluster' => 'redis',
'parameters' => ['password' => env('REDIS_PASSWORD', null)],
],
],
Fair warning, this is all information I just got from research and code diving. While I have used Redis with Laravel, I have not used server side clustering (yet), so this still may not work.
Some useful pieces of information I came across while looking into this:
Predis issue discussing connecting to a redis-cluster:
https://github.com/nrk/predis/issues/259#issuecomment-117339028
It looks like you did not configure Predis to use redis-cluster but instead you are using it with the plain old client-side sharding logic (which is also the default behaviour). You should configure the client setting the option cluster with the value redis to let the client know it must play along with redis-cluster. Quick example:
$client = new Predis\Client([$node1, $node2, ...], ['cluster' => 'redis']);
Doing so will make it possible for the client to automatically handle -MOVED or -ASK responses coming from Redis nodes.
MS article discussing clustering on redis cache:
https://docs.microsoft.com/en-us/azure/redis-cache/cache-how-to-premium-clustering#how-do-i-connect-to-my-cache-when-clustering-is-enabled
You can connect to your cache using the same endpoints, ports, and keys that you use when connecting to a cache that does not have clustering enabled. Redis manages the clustering on the backend so you don't have to manage it from your client.
Laravel code for creating Predis\Client
instances:
https://github.com/laravel/framework/blob/v5.3.28/src/Illuminate/Redis/Database.php#L25-L66