I have a laravel (5.3) app with redis used for sessions (using predis). Everything works as long as I use a single redis node (using default approach from config/database.php). As soon as I switch to a Redis cluster though I am starting to get MOVED error like 50% of the time (based on googling I understand that this should be managed by predis, but somehow isn't).
I tried changing the cluster parameter to true, but then I get a weird error
No connection could be made because the target machine actively refused it. [tcp://127.0.0.1:6379]
Although the redis cluster that I use is deployed in Azure (and is configured via .env file) and the parameters are accepted without any problem when a single node is used.
Configuration
Here is the laravel configuration that I have (as mentioned earlier, it's the standard default)
'redis' => [
'client' => 'predis',
'cluster' => false,
'default' => [
'host' => env('REDIS_HOST', 'localhost'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
],
],
For Redis, I use Azure Redis Cache Cluster Premium P1, 2 shards (as described here).
UPDATE 2
So far I also tried the following variations of the config:
- Setting cluster to true
- Setting cluster to redis
- Adding default -> cluster set to redis
- Adding default -> options set to array('cluster', 'redis')
All the time I am getting MOVED error...
My Redis version is 3.2, predis/predis package 1.1.1
Working config for predis 1.1+
'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)],
],
],
Big thank you for all the help :)
Please refer to [ https://laravel.com/docs/5.5/redis ].
Make sure you have the proper library
In config/database.php as well as config/queue.php [ if your queue is also using clustered reddis]
Related: Laravel + Redis Cache via SSL?
To which I've answered here: https://stackoverflow.com/a/48876398/663058
Relevant details below:
If you have clustering and TLS then you'll need a the following config (tested with AWS Elasticache):
Explaining the above:
'client' => 'predis'
: This specifies the PHP Library Redis driver to use (predis).'cluster' => 'redis'
: This tells Predis to assume server-side clustering. Which just means "follow redirects" (e.g.-MOVED
responses). When running with a cluster, a node will respond with a-MOVED
to the node that you must ask for a specific key.-MOVED
exception 1/n times, n being the number of nodes in Redis cluster (it'll get lucky and ask the right node every once in awhile)'clusters' => [...]
: Specifies a list of nodes, but setting just a 'default' and pointing it to the AWS 'Configuration endpoint' will let it find any/all other nodes dynamically (recommended for Elasticache, because you don't know when nodes are comin' or goin').'options'
: For Laravel, can be specified at the top-level, cluster-level, and node option. (they get combined in Illuminate before being passed off to Predis)'parameters'
: These 'override' the default connection settings/assumptions that Predis uses for new connections. Since we set them explicitly for the 'default' connection, these aren't used. But for a cluster setup, they are critical. A 'master' node may send back a redirect (-MOVED
) and unless the parameters are set forpassword
andscheme
it'll assume defaults, and that new connection to the new node will fail.For AWS elasticcache redis cluster the above configuration did not work, however below are working for me. Also mentioned in the documentation: https://laravel.com/docs/5.4/redis#configuration
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.'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 tofalse
, 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 totrue
, 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
andASK
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 thePredis
client that it needs to handle clustering, and this is done by Laravel through theoptions
array on theredis
configuration.On the
redis
configuration, theoptions
key should be a sibling of your connections (i.e.default
), not a child. Additionally, the options should be specified askey => value
pairs.So, your configuration should look like:
The
cluster
key under theredis
config will tell Laravel to create an aggregatePredis\Client
instance that may handle multiple nodes, and thecluster
key under theoptions
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 toNOAUTH
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:
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
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
Laravel code for creating
Predis\Client
instances:https://github.com/laravel/framework/blob/v5.3.28/src/Illuminate/Redis/Database.php#L25-L66