Saltar al contenido principal

Construye Sistemas Reactivos con Clustron Watch

· 6 min de lectura

La mayoría de los sistemas distribuidos no empiezan siendo complicados.

Empiezan con algo sencillo: un bucle.

Escribes un pequeño trabajador que comprueba si hay trabajo:

while (true)
{
var job = await client.GetAsync<Job>("jobs:next");

if (job.IsSuccess)
{
await ProcessAsync(job.Value);
}

await Task.Delay(1000);
}

En esta etapa, todo parece razonable. La lógica es clara, el sistema se comporta de forma predecible y la carga cognitiva es muy baja.

Pero a medida que el sistema crece, este patrón se convierte silenciosamente en una de las partes más costosas de tu arquitectura.


El coste oculto del sondeo en los sistemas distribuidos​

El sondeo introduce una ineficiencia fundamental: estás preguntando repetidamente al sistema por cambios que normalmente no se han producido.

A medida que escalas:

  • Varias instancias comienzan a sondear los mismos datos
  • Cada instancia realiza lecturas repetidas, la mayoría de las cuales no devuelven información útil
  • Introduces latencia artificial: los cambios solo se detectan en el siguiente ciclo de sondeo
  • El sistema empieza a consumir recursos simplemente para confirmar que nada ha cambiado

Esto conduce a un sistema que es técnicamente correcto, pero operativamente ineficiente.

Y lo que es más importante, el sondeo te obliga a hacer concesiones que no deberías tener que hacer:

  • Sondear con frecuencia → mayor coste, menor latencia
  • Sondear con menos frecuencia → menor coste, mayor latencia

No hay una respuesta correcta, solo un compromiso.


Un modelo diferente: reaccionar en lugar de preguntar​

La alternativa no es solo una optimización: es una forma diferente de pensar en los sistemas.

En lugar de preguntar:

"¿Ha cambiado algo?"

Diseñas tu sistema para que responda a:

"Algo acaba de cambiar."

Este es el cambio de un modelo basado en extracción (pull) a un modelo basado en inserción (push).

En Clustron, esto se implementa mediante Watch.


Modelo de sondeo frente a observación de cambios


Presentando Watch: un flujo de cambios de estado​

Clustron Watch te permite suscribirte a los cambios del sistema y recibir eventos a medida que ocurren.

En esencia, Watch convierte tus datos en un flujo continuo de actualizaciones.

Observar una sola clave​

var (subscription, initialRecord) = await client.Watch.WatchKeyAsync(
"order:1001",
new WatchOptions { IncludeInitialSnapshot = true },
ev =>
{
Console.WriteLine($"{ev.EventType} → {ev.Key} = {ev.Value}");
});

Esto hace dos cosas importantes:

  1. Te proporciona el estado actual (initialRecord)
  2. Te suscribe a todos los cambios futuros

Esta combinación es fundamental. Sin ella, tendrías que obtener manualmente el valor actual y luego suscribirte, lo que introduce condiciones de carrera.


Instantánea + streaming: una garantía de corrección​

Uno de los aspectos sutiles pero esenciales de Watch es la capacidad de incluir una instantánea inicial.

Cuando IncludeInitialSnapshot está habilitado:

  • Recibes el valor actual en el momento de la suscripción
  • Luego recibes todas las actualizaciones posteriores en orden

Esto garantiza que:

  • No pierdas el estado existente
  • No pierdas los cambios futuros
  • No necesites lógica de sincronización adicional

En otras palabras, Watch proporciona una vista completa y consistente de la evolución del estado.


Observar colecciones: más allá de las claves individuales​

Si bien observar una sola clave es útil, la mayoría de los sistemas reales operan sobre grupos de datos relacionados.

Clustron admite esto mediante la observación basada en prefijos:

var subscription = await client.Watch.WatchPrefixAsync(
"jobs:",
new WatchOptions { IncludeInitialSnapshot = false },
ev =>
{
Console.WriteLine($"Job update: {ev.Key}");
});

Esto te permite observar todos los cambios dentro de un grupo lógico.

Desde una perspectiva arquitectónica, aquí es donde Watch se vuelve significativamente más potente:

  • Ya no reaccionas a valores individuales
  • Reaccionas a transiciones de estado de todo el sistema

Comprender el modelo de eventos​

Cada cambio en el sistema produce un WatchEvent.

Un evento típico contiene:

  • La clave que cambió
  • El tipo de cambio (Put o Delete)
  • El nuevo valor (si corresponde)
  • Una revisión que representa la versión del cambio

Esta revisión es particularmente importante. Proporciona un orden y te permite razonar sobre la secuencia de eventos en un sistema distribuido.

En efecto, Watch te proporciona un registro de transiciones de estado, entregado en tiempo real.


Qué permite esto en la práctica​

Una vez que adoptas este modelo, varios problemas comunes se simplifican.

Procesamiento de trabajos​

En lugar de sondear en busca de nuevo trabajo, los trabajadores reaccionan de inmediato cuando se crean o actualizan trabajos.

Esto elimina los ciclos inactivos y reduce la latencia.


Invalidación de caché​

En lugar de depender del TTL o de la actualización periódica, las cachés pueden actualizarse a sí mismas en respuesta a cambios reales.

Esto mejora tanto la frescura como la eficiencia.


Coordinación distribuida​

Los sistemas que dependen de un estado compartido —como la elección de líder o la propiedad de concesiones— se benefician significativamente de las actualizaciones en tiempo real.

Los cambios se propagan al instante, lo que permite una coordinación más rápida y fiable.


Presencia y supervisión​

El seguimiento de los componentes activos se vuelve trivial:

  • Cuando aparece un nodo → recibes un evento
  • Cuando desaparece un nodo → recibes un evento

Sin sondeo, sin conjeturas.


Consideraciones operativas​

Watch está diseñado para cargas de trabajo de larga duración y orientadas a eventos.

Algunas consideraciones importantes:

Mantén los controladores ligeros​

Tu controlador de eventos no debe realizar trabajo pesado directamente.

En su lugar:

  • Procesa rápidamente
  • Delega las tareas más pesadas a trabajadores en segundo plano

Gestiona el ciclo de vida de la suscripción​

Detén siempre las suscripciones cuando ya no sean necesarias:

await subscription.StopAsync();

Esto garantiza que los recursos se liberen de forma limpia.


Comprende el alcance​

  • Observar una clave → solo esa clave produce eventos
  • Observar un prefijo → solo las claves coincidentes producen eventos

Los eventos están delimitados y aislados, lo que evita interferencias no deseadas entre componentes.


De los bucles de sondeo a los sistemas reactivos​

El cambio del sondeo a la observación no se trata solo de rendimiento.

Cambia la estructura de tu aplicación.

Los sistemas de sondeo se construyen en torno a:

Loop → Check → Wait → Repeat

Los sistemas reactivos se construyen en torno a:

Event → Handler → Action

Esto da lugar a sistemas que son:

  • Más receptivos
  • Más eficientes
  • Más fáciles de razonar

Reflexiones finales​

El sondeo suele ser la forma más sencilla de empezar y, en sistemas pequeños, funciona lo suficientemente bien.

Pero a medida que los sistemas crecen, el coste del sondeo se vuelve cada vez más difícil de justificar.

Clustron Watch ofrece un enfoque diferente: uno en el que tu sistema reacciona al cambio en lugar de buscarlo.

Stop asking your system if something changed.

Let it tell you when it does.

Primeros pasos​

Prueba una observación sencilla:

await client.Watch.WatchPrefixAsync("orders:", null, ev =>
{
Console.WriteLine($"{ev.Key} updated");
});

A partir de ahí, empieza a eliminar los bucles de sondeo, uno por uno.

Rápidamente descubrirás que tu sistema se vuelve no solo más rápido, sino fundamentalmente más simple.