node-redis
node-redis copied to clipboard
Redis create cluster issue
Hello,
I have a Redis MemoryDB cluster with 2 nodes in AWS and I'm trying to connect to it through createCluster
, but the connection isn't established.
Here is how I try to connect:
import { createCluster } from 'redis';
(async () => {
const REDIS_USERNAME = 'test-username';
const REDIS_PASSWORD = 'test-pass';
const cluster = createCluster({
rootNodes: [
{
url: `rediss://redis-region-0001-001.amazonaws.com:6379`,
username: REDIS_USERNAME,
password: REDIS_PASSWORD,
},
{
url: `rediss://redis-region-0001-002.amazonaws.com:6379`,
username: REDIS_USERNAME,
password: REDIS_PASSWORD,
},
]
});
cluster.on('error', (err) => console.log('Redis Cluster Error', err));
console.log('before connection');
await cluster.connect();
console.log('connected to cluster...');
await cluster.set('key', 'value');
const value = await cluster.get('key');
console.log(value);
await cluster.disconnect();
})();
The log connected to cluster...
is never showed up.
If I add in the configuration of createCluster
to the defaults
option the master node endpoint like this:
defaults: {
url: `rediss://redis-region-0001-001.amazonaws.com:6379`,
username: REDIS_USERNAME,
password: REDIS_PASSWORD,
}
The connection is established and everything works fine! Why do I need the defaults option? Shouldn't it work only with rootNodes? Thank you!
Environment:
- Node.js Version: 16-alpine3.12
- Redis Server Version: 6
- Node Redis Version: 4.0.0
As described in the clustering guide:
-
rootNodes
- "An array of root nodes that are part of the cluster, which will be used to get the cluster topology. Each element in the array is a client configuration object. There is no need to specify every node in the cluster, 3 should be enough to reliably connect and obtain the cluster configuration from the server" -
defaults
- "The default configuration values for every client in the cluster. Use this for example when specifying an ACL user to connect with"
node-specific properties should be listed in rootNodes
, the rest in defaults
createCluster({
rootNodes: [{
url: `rediss://redis-region-0001-001.amazonaws.com:6379`
}, {
url: `rediss://redis-region-0001-002.amazonaws.com:6379`
}],
defaults: {
username: REDIS_USERNAME,
password: REDIS_PASSWORD
}
});
I had tried this also and the same thing happened! Could you please reopen the issue?
@ellik95 It didn't work even when the username & password are in the defaults
object? can you please share your code?
Yes, with this code:
import { createCluster } from 'redis';
(async () => {
const REDIS_USERNAME = 'test-username';
const REDIS_PASSWORD = 'test-pass';
const cluster = createCluster({
rootNodes: [
{
url: `rediss://redis-region-0001-001.amazonaws.com:6379`
},
{
url: `rediss://redis-region-0001-002.amazonaws.com:6379`
},
],
defaults: {
username: REDIS_USERNAME,
password: REDIS_PASSWORD,
}
});
cluster.on('error', (err) => console.log('Redis Cluster Error', err));
console.log('before connection');
await cluster.connect();
console.log('connected to cluster...');
await cluster.set('key', 'value');
const value = await cluster.get('key');
console.log(value);
await cluster.disconnect();
})();
it is not connected. Only if I add the url of the master node to the defaults options works. Like this:
defaults: {
url: `rediss://redis-region-0001-001.amazonaws.com:6379`,
username: REDIS_USERNAME,
password: REDIS_PASSWORD,
}
And I was wondering why is this needed.
createCluster({
rootNodes: [{
url: `rediss://redis-region-0001-001.amazonaws.com:6379`
}, {
url: `rediss://redis-region-0001-002.amazonaws.com:6379`
}],
defaults: {
tls: true,
username: REDIS_USERNAME,
password: REDIS_PASSWORD
}
});
It's not working either and tls
option does not exist in type Partial<RedisClusterClientOptions>
.
@ellik95
createCluster({
rootNodes: [{
url: `rediss://redis-region-0001-001.amazonaws.com:6379`
}, {
url: `rediss://redis-region-0001-002.amazonaws.com:6379`
}],
defaults: {
socket: {
tls: true
},
username: REDIS_USERNAME,
password: REDIS_PASSWORD
}
});
Unfortunately it is not working either. Only with the below code is:
createCluster({
rootNodes: [{
url: `rediss://redis-region-0001-001.amazonaws.com:6379`
}, {
url: `rediss://redis-region-0001-002.amazonaws.com:6379`
}],
defaults: {
url: `rediss://redis-region-0001-001.amazonaws.com:6379`
username: REDIS_USERNAME,
password: REDIS_PASSWORD
}
});
Also when I connect I sometimes get the error ReplyError: MOVED 12539 rediss://redis-region-0001-002.amazonaws.com:6379
and I cannot get the value from the key.
Same issue as @ellik95.
I'm trying to connect with a Redis cluster running on Kubernetes and the connection is always ReplyError: MOVED xxxx
In my case I have Load Balancer to connect with, so I'm using createClient
rather than createCluster
.
Connection through redis-client
command line works fine, though.
Is there any specific configuration we have to do with createClient
when using Load Balanced redis cluster?
I'm having the same issue with elasticache and the createCluster
function, it just hangs indefinitely when you call cluster.connect()
unless you provide a url
entry in the defaults
object.
However, when you do that it seems like it only connects to that node. If you try to access a key that gets sorted onto one of the other nodes you get the MOVED error.
@leibale I see that you mentioned this issue on https://github.com/redis/node-redis/pull/1827 but I'm not sure how that would apply to solve this issue. My server has access to the elasticache nodes just fine, it can connect to any of them individually with createClient
but createCluster
isn't able to connect
unless you provide that default url.
@acb this line should fix it
@leibale oh, awesome! Do you have any idea when that might be pushed out in a release?
@acb Sunday/Monday :)
@leibale I just tried release 4.0.4 and unfortunately I'm still having the same problem. With the code
const client = createCluster({
rootNodes: [
{
url: "rediss://redis-1-url:6379",
},
{
url: "rediss://redis-2-url:6379",
},
{
url: "rediss://redis-3-url:6379",
},
],
defaults: {
password: "password123"
}
});
client.on("error", (err: any) => {
console.log("REDIS ERROR");
console.log(err);
});
console.log('connecting to redis');
client.connect().then(() => {
console.log('connected to redis');
});
I see 'connecting to redis' in my logs but it seems to hang and never actually connects. If I update my defaults to be
defaults: {
url: "rediss://redis-1-url:6379",
password: "password123"
}
Then in my logs I get 'connecting to redis' followed immediately by 'connected to redis', but the connection is only to the one redis server that's in the default. If I try to sample random keys 2/3rds of them return with something like MOVED 11388 redis-2-url
.
@acb try this
const client = createCluster({
rootNodes: [
{
url: "rediss://redis-1-url:6379",
},
{
url: "rediss://redis-2-url:6379",
},
{
url: "rediss://redis-3-url:6379",
},
],
defaults: {
password: "password123",
socket: { tls: true }
}
});
@leibale you're a wizard! That did it, I'm getting successful cluster connections now. I thought that having rediss
as the protocol would handle the tls-ing but I guess cluster mode handles things a little differently.
Thanks so much for the help!
For me, I found both the :6379 and the extra s in rediss:// to be unnecessary. This worked:
const client = createCluster({
rootNodes: [
{
url: "redis://redis-1-url",
},
{
url: "redis://redis-2-url",
},
{
url: "redis://redis-3-url",
},
],
defaults: {
password: "password123",
socket: { tls: true }
}
});
I am still getting the Error MOVED 12218 ip:6379
.
Following is the code
import {createClient} from "redis"; // redis: ^4.0.1
const client = createClient({url: "redis://xyz.abc.clustercfg.use2.cache.amazonaws.com:6379"});
await client.connect();
console.log("client connected");
console.log(await client.ping());
I do get the output : "client connected" and "PONG".
But when i do await client.get()
or await client.set()
i get the MOVED error.
This is to way to connect for above issue.
const redis = require('redis');
const client = redis.createCluster({
rootNodes: [
{
url: `redis://${ConfigurationEndpoint}:${port}`,
},
],
useReplicas: true,
});
Has this ever been addressed? I can't go to production with MemoryDB if this isn't fixed, and the approaches noted here result in an infinite loop of trying connection to localhost.
@nibblesnbits none of these worked for you? https://github.com/redis/node-redis/issues/1782#issuecomment-1048241231 https://github.com/redis/node-redis/issues/1782#issuecomment-1003704869
wanna share some code?
Well it looks like lack of sleep and attention to detail killed me. I missed that the call was createCluster
, not createClient
. 🤦♂️
But now I'm curious; is it more correct to use the cluster(s)' hostname, or the individual nodes? Seems logical that the latter is more correct.
For anyone trying to connect to a MemoryDb cluster using TLS, this has proven to be the solution:
const redisClient = redis.createCluster({
rootNodes: [{
url: `redis://${redisHost}:6379`
}],
useReplicas: true,
defaults: {
socket: { tls: true }
}
});
Well it looks like lack of sleep and attention to detail killed me. I missed that the call was
createCluster
, notcreateClient
. 🤦♂️But now I'm curious; is it more correct to use the cluster(s)' hostname, or the individual nodes? Seems logical that the latter is more correct.
AFAIK you want to use the cluster hostname(cluster endpoint), especially for MemoryDb where you may add or delete nodes dynamically.
For me, I found both the :6379 and the extra s in rediss:// to be unnecessary. This worked [...]
@killdash9 correct. Not sure what the implications are, but redis://
instead of rediss://
appears to work. Port seems unnecessary too.
With those changes in mind, my code now becomes:
import { createCluster } from "redis";
const socket = {
tls: true,
rejectUnauthorized: false, // Disable certificate validation
};
// Assuming you're using a Redis client that supports cluster mode and TLS
const redisClient = createCluster({
rootNodes: [
{
url: "redis://<shard #1, node #1 endpoint>",
},
{
url: "redis://<shard #2, node #1 endpoint>",
},
{
url: "redis://<shard #3, node #1 endpoint>",
},
],
defaults: {
password: "TODO",
socket,
},
});
// Handle errors
redisClient.on("error", (err) => {
console.error("Redis Cluster Error", err);
});
// Connect to the cluster
await redisClient.connect().catch(console.error);
Example of <shard #2, node #1 endpoint>
example-0002-001.example.abc123.use1.cache.amazonaws.com