Por qué un layout aparte
Un Redis Cluster reparte el espacio de claves en 16384 hash slots entre varios masters. Dos reglas rompen el layout histórico ('legacy'):
- un script, un
MULTIo un comando con varias claves solo puede tocar claves de un slot; si no, el servidor respondeCROSSSLOT; KEYSySCANsolo ven un nodo, así que un listado basado en ellos queda incompleto sin avisar.
'cluster' resuelve las dos. Cada grupo de claves que toca un script
comparte un hash tag ({…}), y los listados leen índices (sorted sets)
que viven en el mismo slot que sus registros, así que son exactos incluso
mientras se mueven slots.
Configuración
Pasa un cliente cluster de node-redis, o deja que el componente cree uno:RedisCoordinator y RedisDurability aceptan la misma opción cluster, que
no se puede combinar con url ni con client. Un cliente que crea el propio
componente (desde url o cluster) siempre lleva un listener de 'error', así
que una conexión caída o un failover se reintentan en vez de tumbar el proceso;
pasa logger para ver esos errores. Un cliente que pasas tú es tuyo: ponle un
listener de 'error', como arriba.
Reglas:
layout: 'legacy'con un cliente cluster lanzaRangeError.- En el layout cluster el prefijo no puede llevar
{ni}(rompería los hash tags). layout: 'cluster'también funciona en un solo nodo: así se migra un keyspace existente en el sitio, y así puedes probar el layout en local.store.coordinator(),store.schedules()ystore.leases()heredan el layout del store. Usa el mismo layout y prefijo en todas las réplicas.- Cambia todas las réplicas a la vez. Réplicas con layouts distintos sobre
un mismo prefijo ven keyspaces distintos, y sus turnos y locks dejan de
excluirse entre sí. Un
RedisStorecon layout legacy cuyo prefijo lleva el marcador de layout avisa enstart()(logger.warn, o si nologger.error); aun así arranca.
Layout de claves
p es el prefijo. Los segmentos variables van con percent-encoding (no queda
ningún :, {, } ni carácter de glob), así que un id de chat nunca rompe un
hash tag.
El bucket de un registro es FNV-1a de su id módulo 32 (parte del contrato
cluster:v1). Los tags de bucket no llevan prefijo, así que los bots que
comparten un cluster reparten cada tipo en los mismos 32 slots. Son unos 10 por
master en un cluster de 3 masters, y un listado cuesta 32 comandos.
Qué cambia por dentro
- Cada script declara todas sus claves (el store comprueba que comparten slot
antes de la llamada). El layout cluster usa scripts en lugar de
MULTI. deleteChatborra los mensajes en tandas de 1000, cada tanda atómica: un item solo se va junto con su entrada del log. El registro del chat va al final. Un mensaje guardado durante el borrado sobrevive intacto. Ya no es un único script, porque el log y el registro viven en slots distintos.- Los índices de checkpoints y de datos de usuario llevan como score la caducidad del servidor y se podan al leer, así que las entradas caducadas desaparecen de los listados igual que las claves caducadas.
- Un coordinador que espera turno no puede leer desde su script el heartbeat de
otra réplica (otro slot). El script del turno informa del titular anterior, y
el coordinador consulta su heartbeat con una llamada aparte de una sola clave
antes de saltar su ticket. La exclusión mutua sigue basándose en la
comprobación atómica de la clave
owneren el slot del chat. - Las respuestas
TRYAGAINyCLUSTERDOWN, y una redirecciónMOVED/ASKque node-redis no siguió, se reintentan con backoff (10 intentos, unos 2,5 s; se ajusta conwithClusterRetry(client, { attempts, baseMs })pasando el cliente envuelto). Esas respuestas llegan antes de que se ejecute nada. Una respuesta perdida nunca se reintenta.
Notas sobre node-redis 4
El paquete esquiva dos rarezas del cliente cluster de node-redis 4.x:cluster.eval(script, { keys })se enruta por el argumento equivocado, así que puede caer en un master al azar y no seguir nunca elMOVED. Los componentes envían elEVALenrutado por su primera clave.- Tras un cambio de topología, un
MULTIpuede fallar conEXECABORTsin que node-redis vuelva a leer la topología. El layout cluster no usaMULTI.
Migrar un keyspace existente
No hay lectura dual de los dos layouts: rompería la atomicidad de las escrituras compare-and-set, de los fences y de los índices. La migración se hace en frío.-
Para todos los bots del prefijo. El helper se niega mientras exista un
heartbeat de coordinador o un ticket pendiente en el origen, o en un destino
distinto, salvo con
allowLive: true. -
Copia el keyspace:
-
Arranca los bots con el layout cluster (
layout: 'cluster'en el nodo único, o un cliente cluster). Tras una migración en el sitio puedes mover los datos al cluster con tus herramientas habituales. -
Cuando lo hayas verificado, borra las claves de origen con
migrateToClusterLayout({ source, prefix, deleteSource: true }).
- Mensajes: se leen desde el log de cada chat (nunca interpretando claves de
item). Los items que no pertenecen a su chat se saltan, y los que ningún log
referencia se listan en
report.orphans. - Checkpoints y datos de usuario conservan el TTL que les quedaba.
- Fences de locks y terms de liderazgo se suben con “fijar si es mayor”, así que nunca retroceden. Los contadores de turno son efímeros y no se copian.
- Schedules, incluidos los marcadores de borrado, conservan su token de
fencing, que nunca retrocede: un registro de origen con un token mayor
reemplaza al del destino aunque no pases
overwrite, y un registro sobrescrito conserva el token mayor. - Conversaciones, cursores, leases y el journal de durabilidad conservan su nombre. Solo se copian si el destino es otro servidor u otro prefijo.
- Idempotente y reanudable: una clave de destino que ya existe gana, salvo
con
overwrite: true. El marcador de layout se escribe al final, y solo en una pasada completa: una pasada limitada conincludeno lo escribe (report.layoutMarkerdice qué pasó), así que la comprobación destart()de abajo sigue activa hasta copiar todos los namespaces.
allowLegacyData: true para arrancar igualmente.
Vuelta atrás
direction: 'cluster-to-legacy' copia el layout cluster de vuelta al legacy. El
destino es un solo nodo; el origen puede ser un solo nodo o un cliente Redis
Cluster (se recorre cada master), así que también se puede revertir un
keyspace que ya vive en un cluster.
Por defecto la vuelta atrás refleja el origen (mirror: true): el destino
legacy queda igual que el origen con layout cluster. Los registros se
sobrescriben, y los registros legacy que el origen ya no tiene se borran
(report.pruned). Sin eso, las claves legacy que dejó la pasada de ida (las
conserva salvo con deleteSource) ganarían a escrituras más nuevas, y lo
borrado después (deleteChat, clearCheckpoint, borrados de ctx.user)
volvería a aparecer. Un schedule que solo existe en el layout legacy pasa a ser
un marcador de borrado que conserva su token de fencing; los fences de locks y
los terms de liderazgo solo suben. Lánzala antes con dryRun: true para ver
los conteos. Pasa mirror: false para una vuelta atrás que solo copia (las
claves legacy existentes ganan).
Una vuelta atrás completa también borra el marcador de layout, así que un store
con layout cluster arrancado por error rechaza los datos legacy restaurados.
Antes de volver a migrar hacia el layout cluster, borra las claves con layout
cluster (vuelve atrás con deleteSource: true); si no, los registros cluster
viejos ganarán en la siguiente pasada de ida.
Límites
- Replicación asíncrona. Igual que con Sentinel, un failover puede perder
las últimas escrituras confirmadas: un lease concedido dos veces, una
actualización compare-and-set perdida, un fence o un term repetidos. No es
Redlock. Configura
min-replicas-to-write 1y apóyate en la idempotencia del outbox durable para los envíos. - Cada tabla de durabilidad vive en un slot (
{durable:<tabla>}), como antes. - Movimiento de slots: los listados siguen siendo exactos. Las escrituras de
varias claves sobre un slot en movimiento reciben
TRYAGAINy se reintentan hasta que termina el movimiento. - Se lee solo de los masters (
useReplicas: false): los bucles compare-and-set y los checkpoints tienen que ver sus propias escrituras.
Probar en local
CONTRIBUTING.md tiene los detalles.
Ver también Varias réplicas y Procesamiento durable.