redis / node-redis

Redis Node.js client
https://redis.js.org/
MIT License
16.85k stars 1.87k forks source link

CreateCluster: Cannot read property 'master' of undefined when used with bitnami/redis-cluster #2187

Open nimishnz opened 2 years ago

nimishnz commented 2 years ago

I am new to Redis and Node-redis. I have setup local redis using bitnami image. Below is a part of docker-compose.yml

  redis:
    image: bitnami/redis-cluster:7.0
    restart: always
    ports:
      - '6379:6379'
    expose:
      - 6379
    healthcheck:
      test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
      interval: 10s
      timeout: 5s
      retries: 5
    environment:
      ALLOW_EMPTY_PASSWORD: yes
      REDIS_CLUSTER_REPLICAS: 1
      REDIS_NODES: redis
      REDIS_CLUSTER_CREATOR: yes
      REDIS_CLUSTER_ANNOUNCE_IP: localhost
      REDIS_CLUSTER_DYNAMIC_IPS: no

Below is nodejs program to initialise redis cluster and add items to list.

  const registerEventHandlers = async (redisClient) => {
    redisClient.on("connect", () => console.log("Redis Client connected"));
    redisClient.on("ready", () => console.log("Redis Client is ready to use"));
    redisClient.on("reconnecting", () => console.log("Redis Client reconnecting"));
    redisClient.on("error", (err) => console.log(`Redis Client Error : ${err}`));
    redisClient.on("end", () => console.log("Redis Client is gracefully terminated"));
  };

    const redisClient = redis.createCluster{
      rootNodes: [
        {
          url: "redis://localhost:6379"
        }
      ],
      useReplicas: true,
      defaults: {
        socket: {
          connectTimeout: 5000,
          reconnectStrategy: (attempts) => attempts > 3 ? new Error("Max retry attempt has been reached") : 1000
        }
      }
    });
    await registerEventHandlers(redisClient);
    await redisClient.connect();

    // Fails with undefined master error
    const result = await redisClient.rPush(key, items); // items could be string or array of string e.g. ["test1", "test2"]

    // Fails with undefined length error
    const result = await redisClient.sendCommand(["LPOP", key, count.toString()]);

   // ignores the count and always returns only 1 item from the list
    const result = await redisClient.lPop(key, count);

This program work fine if we use createClient instead of createCluster to connect to bitnami redis cluster container. yeah lPop ignores the count and always returns/pops only 1 item from list. aport from that no errors/exceptions as such.

if createCluster is used, rPush, lPop fails with error **Cannot read property 'master' of undefined**: This error I can reproduce with my local docker-setup.

This exact same code works fine when used against production server (AWS ElastiCache Redis Cluster, Engine Version:6.2.6). So I strongly believe this could be due to my docker-compose setup.

Please suggest me if I am doing anything wrong or way to fix this issue. Complete stack:

TypeError: Cannot read property 'master' of undefined
      at RedisClusterSlots.getSlotMaster (node_modules/@redis/client/dist/lib/cluster/cluster-slots.js:50:81)
      at RedisClusterSlots.getClient (node_modules/@redis/client/dist/lib/cluster/cluster-slots.js:58:25)
      at Commander._RedisCluster_execute (node_modules/@redis/client/dist/lib/cluster/index.js:102:73)
      at Commander.sendCommand (node_modules/@redis/client/dist/lib/cluster/index.js:65:98)
      at Commander.commandsExecutor (node_modules/@redis/client/dist/lib/cluster/index.js:62:75)
      at Commander.BaseClass.<computed> [as rPush] (node_modules/@redis/client/dist/lib/commander.js:8:29)

sendCommand also seems to fail in case of cluster. Fails against AWS redis server.

const result = await redisClient.sendCommand(["LPOP", key, count.toString()]);
                                    or
const result = await redisClient.sendCommand(["LPOP", key]);
Both fails with below error:

/home/ubuntu/redistest/node_modules/@redis/client/dist/lib/client/RESP2/encoder.js:6
    let strings = '*' + args.length + CRLF;
                             ^

TypeError: Cannot read properties of undefined (reading 'length')
    at encodeCommand (/home/ubuntu/redistest/node_modules/@redis/client/dist/lib/client/RESP2/encoder.js:6:30)
    at RedisCommandsQueue.getCommandToSend (/home/ubuntu/redistest/node_modules/@redis/client/dist/lib/client/commands-queue.js:187:45)
    at Commander._RedisClient_tick (/home/ubuntu/redistest/node_modules/@redis/client/dist/lib/client/index.js:434:76)
    at Commander._RedisClient_sendCommand (/home/ubuntu/redistest/node_modules/@redis/client/dist/lib/client/index.js:418:82)
    at Commander.sendCommand (/home/ubuntu/redistest/node_modules/@redis/client/dist/lib/client/index.js:173:100)
    at /home/ubuntu/redistest/node_modules/@redis/client/dist/lib/cluster/index.js:65:148
    at Commander._RedisCluster_execute (/home/ubuntu/redistest/node_modules/@redis/client/dist/lib/cluster/index.js:105:26)
    at Commander.sendCommand (/home/ubuntu/redistest/node_modules/@redis/client/dist/lib/cluster/index.js:65:98)
    at /home/ubuntu/redistest/app.js:27:33
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

Please help me to proceed in this case. Apologies if this is already being discussed, please point me to the same. Thanks for the support.

Environment:

MacOS 12.4, Tests against Bitnami Redis Cluster.

Ubuntu 22.04 LTS, Tests against AWS ElastiCache Redis Cluster.

nimishnz commented 2 years ago

Update: Found lPopCount method which pops count of items from queue. So we are good with lPop issue.

jeffgunderson commented 2 years ago

I had a similar issue although I was using https://github.com/Grokzen/docker-redis-cluster for the cluster. By looking at the docker logs I saw that unlike elasticache, there are multiple root nodes created, so I needed to both expose those ports in my docker-compose, and then add them to rootNodes.

in my docker-compose

ports:
      - '6379:6379'
      - '6380:6380'
      - '6381:6381'

and then in code(I had a comma delimited env variable ELASTICACHE_HOST=localhost:6379,localhost:6380,localhost:6381)..

this.cluster = createCluster({
    rootNodes: process.env.ELASTICACHE_HOST.split(',').map((url) => ({ url: `redis://${url}` })),
  });

I'm not sure this is exactly the issue you're experiencing but hope it helps! I found this issue because I'm getting the same master of undefined error but in AWS ElastiCache and it happens very sporadically.