Saltar al contenido

Blog técnico | Patrones de un contexto limitado eventualmente consistente: Curación fuera de la banda

Cuando se trabaja en un sistema distribuido, el sistema general está compuesto por componentes discretos. Estos componentes pueden tener diferentes nombres, por ejemplo, microservicios o subsistemas. Voy a llamar a estos componentes discretos “contextos delimitados”, un término tomado del libro de Eric Evan Domain Driven Design1.

A veces estos contextos delimitados tienen la necesidad de ser eventualmente consistentes2. Esta serie de artículos es un intento de catalogar los patrones comunes que he encontrado al trabajar con contextos delimitados eventualmente consistentes.

Blog técnico | Patrones de un contexto limitado eventualmente consistente: Curación fuera de la banda
Blog técnico | Patrones de un contexto limitado eventualmente consistente: Curación fuera de la banda

En esta entrega cubriremos la Curación Fuera de Banda, un patrón que puede ser usado para reducir el acoplamiento temporal al curar sus cachés del lado del servidor.

La casa que construyó el acoplamiento temporal

Has construido un contexto delimitado que maneja las peticiones entrantes. Tu cliente te llama, y para emitir una respuesta a tu cliente primero tienes que llamar a otro contexto delimitado. Tu contexto acotado tiene una dependencia de otro contexto acotado. La arquitectura podría parecerse a esto:

Dentro de esta arquitectura, como en todas las arquitecturas, hay un acoplamiento. Hay un tipo particular de acoplamiento en el que me gustaría centrarme. Antes de que podamos responder a una petición del cliente necesitamos llamar a nuestra dependencia. En otras palabras, no podemos responder a nuestro cliente hasta que hayamos recibido una respuesta de nuestra dependencia. Esta es una forma de acoplamiento temporal . Podemos definir el acoplamiento temporal como “el grado en que el envío y el manejo de un mensaje están conectados en el tiempo “3. Este acoplamiento produce una realidad interesante para nosotros: nuestro estado, en cualquier momento, depende del estado de nuestra dependencia.

Para estar seguros, esta arquitectura está bien si se tienen respuestas satisfactorias a las siguientes preguntas:

  • ¿Está bien si el cliente espera mientras haces una llamada sincrónica a la dependencia?
  • ¿Qué se hace si la dependencia se reduce o no está disponible en el momento oportuno?

Puede ser que pueda devolver un error y/o una respuesta por defecto al cliente cuando no pueda comunicarse con la dependencia. Si ese es el caso, creo que hemos terminado aquí. Sin embargo, si un código de error o una respuesta por defecto no es una solución que se ajuste a sus limitaciones, hablemos del caching.

Cobrando con caché

Una gran manera de empezar a reducir un acoplamiento temporal es introducir el caching. Aplicado aquí, nuestra arquitectura podría evolucionar en algo como esto:

Cuando recibamos una solicitud, obtendremos los datos necesarios de nuestra dependencia. Antes de responder a nuestro cliente, guardaremos esos datos en nuestro caché. La próxima vez que llegue una solicitud similar, en lugar de ir hasta nuestra dependencia, usaremos los datos encontrados en nuestro caché. El tiempo que mantengamos estos datos en nuestro caché es específico para cada contexto, puede ser de 5 minutos, puede ser de 5 días. La cantidad de tiempo que mantenemos estos datos en nuestro caché es el grado exacto en el que hemos reducido nuestro acoplamiento temporal.

Aunque hemos reducido nuestro acoplamiento temporal hasta cierto punto, aún no lo hemos eliminado totalmente. Cada vez que no somos capaces de utilizar los datos de nuestra caché para responder a nuestro cliente, tenemos que llamar a nuestra dependencia para obtener los datos que necesitamos – hola acoplamiento temporal.

Puede que esté perfectamente bien con este grado de acoplamiento temporal. Si estás familiarizado con el teorema CAP4, esta es una gran arquitectura si estás optimizando para la consistencia . Sin embargo, puede ser que no estés optimizando para la consistencia, o tal vez este grado de acoplamiento temporal no sea aceptable. Si ese es el caso, sigamos caminando hacia la luz.

Curación en banda

Necesito tomarme un momento para definir un término. Cuando un cliente hace una solicitud a nuestro contexto delimitado, todo lo que sucede antes de emitir una respuesta a ese cliente se considera “en banda”. Si no podemos entregar una respuesta a nuestro cliente basándonos en los datos de nuestro caché, necesitamos curar nuestro caché obteniendo datos de nuestra dependencia. En otras palabras, no respondemos a nuestro cliente hasta que hayamos curado nuestro caché. Esto se conoce como “curación en banda”.

Kick Cache Out of the Band

Todavía tenemos un acoplamiento temporal a nuestra dependencia cada vez que necesitamos realizar una curación en banda. Para eliminar este acoplamiento, necesitamos mover la curación fuera de la banda. Nuestra tercera y última iteración se ve así:

Cuando se solicitan datos de nuestro caché, una de dos cosas va a suceder. O bien tendremos un error en el caché, o tendremos un golpe en el caché. Un cache-hit ocurre cuando hay datos válidos en nuestro caché para una solicitud determinada. Un error de caché ocurre cuando:

  • No hay datos en nuestro caché.
  • Hay datos en nuestro caché pero tiene un TTL expirado.

Un TTL para datos en caché es simplemente un tiempo asociado a un registro de caché determinado que determina el tiempo que se debe considerar el registro actualizado. Un TTL caducado nos dice que tenemos que realizar alguna acción en los datos almacenados en la caché.

Si experimentamos una falta de memoria al leer los datos de nuestra memoria, tenemos que decidir cómo responder a nuestro cliente. Si la falta de memoria caché se debe a que los datos han caducado, podríamos responder con los datos caducados. Si el error de la memoria caché se debe a que no hay datos5, podemos emitir una respuesta predeterminada a nuestro cliente, o un error. Además de manejar el error de caché, también publicaremos un evento de “error de caché”. Aquí es donde el “curandero” entra en juego. El curandero está suscrito al evento “cache-missed”. Al recibir el evento “cache-missed”, el curandero recuperará los datos necesarios de nuestra dependencia y los escribirá en nuestro caché, asegurándose de que tenemos datos válidos para la próxima vez que llegue una solicitud similar.

La parte crucial a tener en cuenta es que el curandero está operando fuera de la banda. Cuando experimentamos una falla en el caché, no nos damos la vuelta y llamamos a nuestra dependencia. En su lugar, respondemos lo mejor que podemos y planteamos un evento de falta de memoria que será manejado asincrónicamente, o fuera de banda. Si examinan el diagrama final, notarán que ya no estamos directamente acoplados a nuestra dependencia. Nuestro acoplamiento temporal se ha eliminado por completo, nuestro contexto delimitado puede seguir funcionando aunque nuestra dependencia sea completamente inalcanzable.

Conclusión

La curación fuera de banda nos permite eliminar el acoplamiento temporal que está presente cuando se curan las cachés del lado del servidor. Espero haber demostrado el razonamiento detrás de este patrón y dejarlos mejor equipados para usarlo, si es necesario. Mientras que este patrón elimina el acoplamiento temporal, también introduce una complejidad extra. Como con todos los patrones, deberías evaluar el beneficio y asegurarte de que supera el coste para tu contexto dado. En caso de duda, me gusta empezar de forma sencilla y evolucionar mi solución para satisfacer las necesidades que descubro a lo largo del camino.

1: https://www.goodreads.com/book/show/179133.Domain_Driven_Design

2: https://en.wikipedia.org/wiki/Eventual_consistency

3: https://www.infoq.com/news/2009/04/coupling

4: https://en.wikipedia.org/wiki/CAP_theorem

5: Hay estrategias que puedes usar para prevenir fallos de caché debido a que no hay datos en caché. Este será el tema de otro artículo.

Categorías: technicalTags: architecture, messaging, APIs