MOVED error with Redis cluster
Tags: redis, linux
Published: 2022-02-05 09:19:07

## Introduction

In this article I will try to explain a weird error that I had to deal with, working with Redis.
The common message of the error is "(error) MOVED 12933 127.0.0.1:30003". Now let's
see when this could happened.

## Requirements

In order to follow me you maybe will need to have Redis, I will also assume that you are
using some UNIX like distro, but you could find out the equivalents command on Windows by yourself. 
You are a big boy. In total we only need:

* Redis
* OS: Ubuntu, Debian, Centos, etc... you got the idea.

## Basic setup

### Downloading and compiling Redis

**Downloading**

The current stable version of Redis is [6.2.6](https://download.redis.io/releases/redis-6.2.6.tar.gz), you could download it
through this link or making use of curl, like this:

```bash
curl -sO https://download.redis.io/releases/redis-6.2.6.tar.gz
```

**Extracting folder**

```bash
tar -xf redis-6.2.6.tar.gz
```

Now you should have a folder called **redis-6.2.6**, let's position inside of it with `cd` command:

```bash
cd redis-6.2.6
```

**Compiling**

Now we are good to go.

```bash
make
```

In case you want to run the tests inside the redis code, you could run `make test`. There are other parameters that you can pass in the
build, that could be interesting to trying out, but not now.


## Description

In case you are in hurry, let me just summarize it to you. You will find this error when you try to use a redis client on a redis cluster,
without specifying on this redis client that you are actually connecting to a redis cluster and not a normal redis node. Of course, if you just 
read this sentence could be the case that it makes no sense to you, right? Let's go deeper then.

### Reproducing the error

Let's try to reproduce this error without any particular library in any language first, just making use of [redis-cli](https://redis.io/topics/rediscli).

#### Setup

Let's run a Redis cluster locally, it will be easy. If you already compiled the source code of redis in the previous setup, you will
notice that there's a folder called `utils`. Inside this folder there's another one called `create-cluster`, so I'm going to make use
of the script inside this folder to create a cluster, is just the easiest way.

In order to run this scrip you need to be position in the same folder as the script, so let's go into there:

```bash
cd utils/create-cluster/
```

Run the script:

```bash
./create-cluster start
./create-cluster create
```

Yes, like that. Wait what? Is already created? Yep, is code written in C so... A little confusing what they mean by `start` and `create`, usually the order
is first `create` and then `start`, but naming is hard.

In the `create` command it will ask you **Can I set the above configuration? (type 'yes' to accept)**, so type **yes**, you are following a 
tutorial that's the point.

Now we have our cluster locally running.

**How to stop and clean this?**

```bash
./create-cluster stop
./create-cluster clean
```

### Connecting

Let's connect to the cluster with redis-cli.
In the previous step you must notice a line like this one in the logs printed on the `./create-cluster create` command:

**>>> Performing Cluster Check (using node 127.0.0.1:30001)**

In my case the port that I will be trying to connect is **30001**, but in your case could be any other.

```bash
./src/redis-cli -c -p  30001
```

**VERY IMPORTANT HERE**

The `-c` option is for

>   -c                 Enable cluster mode (follow -ASK and -MOVED re-direction).

So right now we are connecting using the cluster mode, so we won't have any problem, the problem will come later
if we try to make operation with a connection that doesn't have this **Cluster mode** enabled. I'll comment more on that later.


Make a `PING` command just to check if everything looks fine,
```bash
127.0.0.1:30001> PING
```

You should get `PONG` as an answer.

### Reproducing error

Now let's back to business ), I will set several **<key> <value>** pairs and later I will try to fetch them

```bash
127.0.0.1:30001> SET name Lola
-> Redirected to slot [5798] located at 127.0.0.1:30002
OK
127.0.0.1:30002> SET name1 Lola
-> Redirected to slot [12933] located at 127.0.0.1:30003
OK
127.0.0.1:30003> SET name2 Manolo
-> Redirected to slot [742] located at 127.0.0.1:30001
OK
127.0.0.1:30001> SET name3 Juan
OK
127.0.0.1:30001> SET name4 Juan
-> Redirected to slot [8736] located at 127.0.0.1:30002
OK
127.0.0.1:30002
```

Take a look at these logs, yes I'm connected to port **30001** but my **<key> <value>** pairs, not always were set at that node. In my case
the value for key `name1`, on command `SET name1 Lola` was located in a slot in node **30003**. I will explain more in advance, for now let's just
reproduce the error.

**Connecting without cluster mode enabled**

Let's try to connect to one of this node without enabling cluster mode, so open another terminal and run a this command:

```bash
./src/redis-cli -p 30001
```

**IMPORTANT AGAIN**

Here the difference is that the option `-c` was not supplied.

Let's make `PING`, can we? Yes, we can!

```bash
127.0.0.1:30001> PING
PONG
```

### THE ERROR

Now comes the cool surprise, at the moment you must be thinking like, 
"Well I just made PING without problem, so I'm sure that I could retrieve those **<key> <value>** that I already pushed, right?"

Let's try to retrieve them with [GET command](https://redis.io/commands/get),

```bash
127.0.0.1:30001> GET name1
```

**ERROR**

```bash
127.0.0.1:30001> GET name1
(error) MOVED 12933 127.0.0.1:30003
127.0.0.1:30001> 
```

Enjoy debugging this on your application, is not fun at all really. You start thinking, like, why? I ran the magical command called `PING` and
everything is fine.

Just to clarify, this is **NOT** an error of the server, this is **YOUR FAULT**, for setting up bad your redis client. 

## Rule of thumb

In general if you are using a redis cluster in your server, you should enabled the cluster mode on your client. There's a lot of alternatives
for this, depending on the Programming Language that you are using. Here is a list from the redis website:
 
* [redis-rb-cluster](http://github.com/antirez/redis-rb-cluster) is a Ruby implementation written by me (@antirez) as a reference for other languages. It is a simple wrapper around the original redis-rb, implementing the minimal semantics to talk with the cluster efficiently.
* [redis-py-cluster](https://github.com/Grokzen/redis-py-cluster) A port of redis-rb-cluster to Python. Supports majority of redis-py functionality. Is in active development.
* The popular [Predis](https://github.com/nrk/predis) has support for Redis Cluster, the support was recently updated and is in active development.
* The most used Java client, [Jedis](https://github.com/xetorthio/jedis) recently added support for Redis Cluster, see the Jedis Cluster section in the project README.
* [StackExchange.Redis](https://github.com/StackExchange/StackExchange.Redis) offers support for C# (and should work fine with most .NET languages; VB, F#, etc)
* [thunk-redis](https://github.com/thunks/thunk-redis) offers support for Node.js and io.js, it is a thunk/promise-based redis client with pipelining and cluster.
* [redis-go-cluster](https://github.com/chasex/redis-go-cluster) is an implementation of Redis Cluster for the Go language using the Redigo library client as the base client. Implements MGET/MSET via result aggregation.
* [ioredis](https://github.com/luin/ioredis) is a popular Node.js client, providing a robust support for Redis Cluster.
* The [redis-cli](https://redis.io/topics/rediscli) utility implements basic cluster support when started with the -c switch.

Related to Golang, the library [go-redis](https://github.com/go-redis/redis), have also support for this.

## Bibligraphy

1. [Redis cluster tutorial](https://redis.io/topics/cluster-tutorial)
2. [Redis cluster specification](https://redis.io/topics/cluster-spec)
3. [Cluster Nodes](https://redis.io/commands/cluster-nodes)

Until next error guys ).