Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redis create cluster issue #1782

Open
ellik95 opened this issue Dec 14, 2021 · 25 comments
Open

Redis create cluster issue #1782

ellik95 opened this issue Dec 14, 2021 · 25 comments
Labels

Comments

@ellik95
Copy link

ellik95 commented Dec 14, 2021

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
@ellik95 ellik95 added the Bug label Dec 14, 2021
@leibale
Copy link
Contributor

leibale commented Dec 20, 2021

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
 }
});

@leibale leibale closed this as completed Dec 20, 2021
@ellik95
Copy link
Author

ellik95 commented Dec 21, 2021

I had tried this also and the same thing happened! Could you please reopen the issue?

@leibale leibale reopened this Dec 21, 2021
@leibale
Copy link
Contributor

leibale commented Dec 21, 2021

@ellik95 It didn't work even when the username & password are in the defaults object? can you please share your code?

@ellik95
Copy link
Author

ellik95 commented Dec 21, 2021

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.

@leibale
Copy link
Contributor

leibale commented Dec 21, 2021

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
  }
});

@ellik95
Copy link
Author

ellik95 commented Jan 2, 2022

It's not working either and tls option does not exist in type Partial<RedisClusterClientOptions>.

@leibale
Copy link
Contributor

leibale commented Jan 2, 2022

@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
  }
});

@ellik95
Copy link
Author

ellik95 commented Jan 2, 2022

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
  }
});

@ellik95
Copy link
Author

ellik95 commented Jan 21, 2022

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.

@lohanbodevan
Copy link

lohanbodevan commented Feb 10, 2022

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?

@acb
Copy link

acb commented Feb 16, 2022

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 #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.

@leibale
Copy link
Contributor

leibale commented Feb 16, 2022

@acb this line should fix it

@acb
Copy link

acb commented Feb 16, 2022

@leibale oh, awesome! Do you have any idea when that might be pushed out in a release?

@leibale
Copy link
Contributor

leibale commented Feb 16, 2022

@acb Sunday/Monday :)

@acb
Copy link

acb commented Feb 22, 2022

@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.

@leibale
Copy link
Contributor

leibale commented Feb 22, 2022

@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 }
    }
});

@acb
Copy link

acb commented Feb 25, 2022

@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!

@killdash9
Copy link

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 }
    }
});

@nixRajan
Copy link

nixRajan commented Jun 9, 2022

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.

@nixRajan
Copy link

This is to way to connect for above issue.

    const redis = require('redis');

    const client = redis.createCluster({
      rootNodes: [
        {
          url: `redis://${ConfigurationEndpoint}:${port}`,
        },
      ],
      useReplicas: true,
     });

@nibblesnbits
Copy link

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.

@leibale
Copy link
Contributor

leibale commented Aug 16, 2023

@nibblesnbits none of these worked for you?
#1782 (comment)
#1782 (comment)

wanna share some code?

@nibblesnbits
Copy link

nibblesnbits commented Aug 16, 2023

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.

@timvi402
Copy link

timvi402 commented Feb 27, 2024

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, 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.

AFAIK you want to use the cluster hostname(cluster endpoint), especially for MemoryDb where you may add or delete nodes dynamically.

@cb-rnag
Copy link

cb-rnag commented Mar 4, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

9 participants