Saltar al contenido principal

Alta Disponibilidad y Clustering

TinyMQ cuenta con una arquitectura de clustering completamente sin master, peer-to-peer (P2P), escrita desde cero. No requiere Apache ZooKeeper, etcd ni ninguna implementación externa de Raft.

Roles de los Nodes

Cada node en un cluster TinyMQ asume uno de tres roles:

  1. Follower: Acepta peticiones de lectura directamente. Redirige las peticiones de escritura (Publish, Ack) al leader mediante HTTP proxy transparente. Comprueba el estado del cluster para asegurarse de que el leader está activo.
  2. Candidate: Si el leader cae, un follower se convierte en candidate y solicita votos a sus peers.
  3. Leader: Elegido por quorum (N/2 + 1). Gestiona todas las escrituras, las replica a los followers y envía heartbeats periódicos.

Elección de Leader

TinyMQ usa un algoritmo de elección inspirado en Raft. Cuando un follower deja de recibir heartbeats durante un período aleatorizado (8–12 segundos), transiciona a candidate, incrementa su term y solicita votos.

El timeout de elección aleatorizado de 8–12 s está diseñado específicamente para dar a entornos orquestados como Kubernetes StatefulSets el tiempo suficiente para arrancar todos los pods antes de que ocurra la primera elección.

Replicación

TinyMQ usa replicación efímera basada en quorum mediante conexiones TCP paralelas.

Cuando se publica un mensaje al leader:

  1. El leader escribe el mensaje en su propio WAL local.
  2. El leader difunde un comando REPLICATE a todos los followers.
  3. El leader espera REPLICATE_ACK de la mayoría (quorum).
  4. Solo cuando se alcanza el quorum, la API HTTP devuelve 201 Created al cliente.

Sincronización de Estado

Cuando un node se desconecta y vuelve a unirse, envía un SYNC_REQ al leader. El leader transmite el snapshot completo del estado por TCP y señala la finalización con SYNC_COMPLETE, asegurando que el node que se reincorpora esté completamente al día antes de comenzar a participar en el quorum de nuevo.

Modo de Drenado Controlado (Mantenimiento)

Antes de apagar un node para mantenimiento (actualizaciones en caliente, cambios de configuración), puedes drenarlo de forma controlada para evitar perder peticiones en vuelo:

# Vía CLI (recomendado — evita drenar accidentalmente el node incorrecto)
tmq cluster drain http://node-2:7800

# Vía REST
curl -X POST http://node-2:7800/api/drain

Una vez drenado, el node devuelve 503 Service Unavailable en todas las peticiones posteriores. Los load balancers y las readiness probes de Kubernetes dejarán de enrutar tráfico hacia él automáticamente. El drenado es permanente durante el tiempo de vida del proceso — reinícialo para recuperarlo.

Comandos del Protocolo TCP

El cluster se comunica a través de un puerto TCP dedicado (por defecto 7901).

ComandoDirecciónPropósito
PINGFollower → CualquieraComprobación de disponibilidad
HEARTBEATLeader → FollowerSuprime elecciones, anuncia liderazgo
REPLICATELeader → FollowerPropaga nuevos mensajes y ACKs
SYNC_REQFollower → LeaderSolicita snapshot de estado completo al arrancar o reincorporarse (boot/rejoin)
SYNC_COMPLETELeader → FollowerSeñala el fin de la transmisión del snapshot de estado
REQUEST_VOTECandidate → CualquieraSolicita voto de liderazgo
BIND_GROUPLeader → FollowerReplica la creación de Consumer Groups en todo el cluster

Seguridad (HMAC-SHA256)

Cuando se establece TINYMQ_CLUSTER_SECRET, cada paquete TCP individual se firma con HMAC-SHA256. Si un peer intenta unirse al cluster o enviar un comando con una firma inválida, la conexión se interrumpe al instante.

CLUSTER_SECRET es obligatorio cuando el clustering está activo

Si TINYMQ_CLUSTER_ADDR está configurado (es decir, el clustering está activo) pero TINYMQ_CLUSTER_SECRET está vacío, el node se negará a arrancar a menos que TINYMQ_CLUSTER_ALLOW_INSECURE=true esté explícitamente establecido. Es una barrera de seguridad estricta — no es solo una advertencia.

El modo standalone (sin TINYMQ_CLUSTER_ADDR) no se ve afectado y arranca normalmente sin necesidad de secret.

La Importancia de TINYMQ_CLUSTER_SELF

En entornos contenedorizados (redes Docker Bridge, Kubernetes), la IP a la que un proceso se vincula (0.0.0.0) raramente es la dirección que otros nodes usan para alcanzarlo.

Si el Node A anuncia 0.0.0.0:7901 como su dirección, el Node B intentará conectarse a 0.0.0.0 y fallará.

Para resolver esto, TINYMQ_CLUSTER_SELF permite a un node desacoplar su dirección de bind de su identidad anunciada. Por ejemplo, en Kubernetes, se establece al nombre DNS del pod (tinymq-0.tinymq-headless:7901). El broker se vincula a 0.0.0.0, pero indica a todos sus peers que lo alcancen mediante el nombre DNS.

Configuración

Variable de EntornoValor por defectoPropósito
TINYMQ_CLUSTER_ADDR0.0.0.0:7901Dirección TCP de bind
TINYMQ_CLUSTER_SELFvacíoIdentidad anunciada (p. ej., tinymq-0:7901)
TINYMQ_CLUSTER_NODESvacíoLista de peers separada por comas
TINYMQ_CLUSTER_SECRETvacíoSecreto compartido HMAC-SHA256 (obligatorio cuando el clustering está activo)
TINYMQ_CLUSTER_ALLOW_INSECUREfalseOmitir el requisito de secret solo para pruebas locales
TINYMQ_CLUSTER_HTTP_ADVERTISEvacíoLa dirección HTTP que usan los followers para hacer proxy al leader
TINYMQ_CLUSTER_REPLICATE_TIMEOUT500msUsa 2s o 3s en Kubernetes por la latencia DNS
TINYMQ_CLUSTER_LEADERfalseEstablece a true para forzar liderazgo estático (desactiva las elecciones)