Redis 3.0 Cluster: Mayor Disponibilidad para Redis

El desarrollo de Redis ha sido dirigido siempre hacia la velocidad. Cada comando en la página de ayuda tiene la medida de la complejidad en base a poder medir rendimiento. Sorprende que finalmente hayan decidido lanzar la configuración en cluster pero, ¿qué ventajas y desventajas tiene?

Redis es la base de datos en memoria más rápida y popular actualmente. Las características de la base de datos la han hecho una opción bastante aceptada para tareas como caché, mantener la sesión de usuarios e incluso chat entre usuarios.

Hablamos hace unos años de Redis e hicimos un repaso a sus características en este artículo. Ahora hablaremos de su nueva característica de cluster.

Teorema CAP

Aunque me gustaría hablar de este teorema en otro artículo (lo haré), comentamos al igual que ya hice en este otro artículo sobre Riak, en qué consiste:

Las tres propiedades deseadas de un sistema distribuido son: consistencia, disponibilidad y partición (tolerancia a fallos). El teorema establece que puedes solo puedes tener dos de las tres propiedades en cada momento.

En caso de Redis Cluster las dos elecciones han sido disponibilidad (A del inglés availability) y tolerancia a particiones (P del inglés partition-tolerance). En la documentación del cluster dicen no tener fuerte consistencia, pero realmente su consistencia no es ni tan siquiera eventual como el caso de Riak y podríamos perder información.

No obstante esto también puede suceder si tenemos un solo Redis funcionando y apagamos antes de que pueda hacerse el volcado a disco de la última información en memoria.

¿Cómo funciona?

La información se parte en slots. Estos slots son distribuidos entre los servidores que conforman el cluster. Cuando se realiza una petición a un redis la petición es redireccionada al servidor que contiene el slot y es quien responde.

Si un servidor cae el resto de servidores recibe la desconexión y reorganizan los slots sirviéndose de una réplica activa que hubiese del servidor caído.

Configuración del Cluster

Iremos paso a paso siguiendo en parte el tutorial oficial (en inglés) de Redis Cluster. Es importante conocer la configuración aunque siempre podemos usar un comando a modo de asistente que nos guíe para crear la configuración de forma automática (ver create-cluster).

Comenzaremos con este fichero de configuración:

port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes

Vamos a crear 6 directorios y dentro de cada uno escribiremos un fichero redis.conf con el contenido de arriba, pero modificando la línea del puerto para ir de 7000 a 7005.

Esto nos da una estructura de directorios como la siguiente:

.
├── 7000
│   └── redis.conf
├── 7001
│   └── redis.conf
├── 7002
│   └── redis.conf
├── 7003
│   └── redis.conf
├── 7004
│   └── redis.conf
└── 7005
    └── redis.conf

Ahora ejecutamos cada una de las 6 instancias en una terminal diferente con el comando:

$ cd 7000
$ redis-server redis.conf

El último paso para tener nuestro cluster configurado es emplear el script redis-trib (que se encuentra en la distribución de redis) y ejecutar lo siguiente:

/usr/share/doc/redis-tools/examples/redis-trib.rb \
    --replicas 1 \
    127.0.0.1:7000 \
    127.0.0.1:7001 \
    127.0.0.1:7002 \
    127.0.0.1:7003 \
    127.0.0.1:7004 \
    127.0.0.1:7005

La opción replicas indica el número de réplicas que queremos crear. En este caso será solo 1. Lo cual indica que si caen dos servidores y uno guardaba la réplica del otro perderíamos información. Es más, el cluster deja de funcionar.

Jugando un poco

Una vez tenemos el cluster activo podemos jugar un poco conectándonos a través del cliente de redis de la siguiente forma:

redis-cli -c -p 7000

La opción -c nos permite acceder al modo cluster. Conectamos en principio al servidor del puerto 7000, pero realmente podemos conectar con cualquier otro.

Hacemos algunas peticiones:

127.0.0.1:7000> set foo var
-> Redirected to slot [12182] located at 127.0.0.1:7002
OK
127.0.0.1:7002> set hello world
-> Redirected to slot [866] located at 127.0.0.1:7000
OK
127.0.0.1:7000> get foo
-> Redirected to slot [12182] located at 127.0.0.1:7002
"var"

Se puede observar cómo cambia la conexión (en el prompt) del servidor del puerto 7000 al servidor del puerto 7002 cuando el dato (por su hash) debe almacenarse en el slot 12182. El siguiente dato (hello) es para el slot 866 y va de nuevo al servidor 7000. Al solicitar foo de nuevo cambiamos de nuevo la conexión.

Obviamente si conectamos desde nuestro código en PHP, Ruby, Python, Perl, Java, Erlang, Elixir, … la librería de Redis que empleemos tiene que soportar las extensiones de cluster (ASK y MOVED).

Vamos a seguir jugando y vamos a tirar el servidor 7002. En los otros servidores aparece esto:

2532:M 19 May 17:59:31.154 * FAIL message received from 36957c61855e7e35ce9f64dd194c01739d18d808 about f82ded813046d1ca575238e48d0ed58263ffc6c7
2532:M 19 May 17:59:31.154 # Cluster state changed: fail
2532:M 19 May 17:59:31.707 # Failover auth granted to f0e4f9eabdc83c6622dfa6602fd4776c6384030c for epoch 7
2532:M 19 May 17:59:31.747 # Cluster state changed: ok

El cluster se configura como fallido hasta que se soluciona el problema haciendo master la copia almacenada en el servidor 7005. Si estábamos conectados al servidor 70002 obviamente no obtendremos respuesta:

127.0.0.1:7002> get foo
Could not connect to Redis at 127.0.0.1:7002: Connection refused

Pero si conectamos de nuevo con un servidor operativo:

$ redis-cli -c -p 7000
127.0.0.1:7000> get foo
-> Redirected to slot [12182] located at 127.0.0.1:7005
"var"
127.0.0.1:7005>

Obtenemos el cambio al servidor 7005 y el dato que necesitábamos.

Conclusiones

Para emplear el cluster de redis de forma transparente necesitamos no solo configurar el cluster de forma apropiada sino también tener una buena implementación de parte de la librería de cliente que pueda detectar cuándo una conexión no es válida y pueda emplear siempre otra conexión y entender las redirecciones propuestas por los servidores del cluster para encauzar la pregunta al servidor correcto.

En realidad este sistema es una mezcla de varios elementos bastante comunes a la hora de emplear Redis: replicación master-slave y sharding. La ventaja es la facilidad de obtenerlo todo a nivel de servidor y tener que hacer menos a nivel de cliente.

¿Has necesitado un sistema de base de datos en memoria rápida para algún proyecto?, ¿te ha llamado la atención poder disponer de un cluster de redis?, ¿necesitas ayuda para la configuración o implementación de los clientes? ¡Déjanos un comentario!