Son unos patrones de fallo. De esta manera se puede mejorar la reacción y en el mejor de los casos, que el sistema funcione correctamente de nuevo, o al menos que reduzca los efectos del fallo.
Los patrones deben aplicarse durante el diseño para que su posterior implementación sea correcta.
Hay muchos mas patrones, pero consideramos que estos son los que más debemos tener en cuenta.
En algunos casos es útil combinar más de uno.
Esta dirigido a las situaciones en donde se producen pequeños fallos.
No tiene sentido aplicar este patrón en todas las circunstancias, es necesario tener en cuenta la causa del fallo para considerar si tiene sentido o no hacer un reintento.
Puede ser útil en determinadas circunstancias establecer un pequeño tiempo de espera entre los reintentos, para que el servicio destino tenga tiempo para recuperarse.
Ejemplo: Tenemos un servicio A que intenta conectar con un servicio B, la instancia del servicio que va a tratar la petición del servicio B falla, dando un error como resultado de la petición. El servicio A, en vez de propagar o tratar directamente el error, reintenta la petición. En esta ocasión, la petición va a una instancia distinta permitiendo la ejecución satisfactoria.
Busca evitar un fallo en cascada cuando una petición a un servicio da error, generando una respuesta sintética que permita simular la respuesta.
La respuesta sintética, por lo general, debe ser lo más "vacía" posible, indicando la ausencia de información.
Ejemplo:
El servicio A realiza una petición al servicio B, que falla devolviendo un error.
El servicio A reacciona ante esta situación simulando una respuesta ‘estándar’ del servicio B, y continúa tomando la ejecución como válida. El resultado global de la operación es correcto (como si no hubiera habido ningún fallo).
Evita un fallo en cascada cuando una petición a un servicio da error.
Se debe establecer un "circuito" con un estado a la comunicación entre los servicios, de forma que se pueda abrir o cerrar en función del comportamiento del servicio destino.
Se necesita incorporar una pieza lógica que controle el resultado de las peticiones y que permitiría la apertura o cierre del circuito.
Ejemplo: El servicio A realiza peticiones al servicio B, las cuales devuelven errores en la ejecución. Ante esta situación, el ‘circuito’ se abre y cualquier petición hacia el servicio B se sustituye por una respuesta ‘por defecto’ (bien un error directo o utilizando el patrón fallback, con una respuesta sintética que permita continuar la ejecución controlada del servicio A).
Periódicamente, el circuito da paso a algunas peticiones para comprobar si el servicio B se ha recuperado. Si las peticiones se ejecutan satisfactoriamente, el circuito se cierra dejando pasar todas las siguientes peticiones. En caso contrario, el circuito quedará abierto.
Es el más básico, por ello esta implementado en la mayoría de los frameworks con valores por defecto.
Evita esperas "infinitas" en las peticiones a los servicios en situaciones de fallo, por si una respuesta da error o fallo.
Ejemplo: El servicio A realiza una petición al servicio B. El servicio B sufre un problema durante el proceso de la petición, produciendo que la petición se queda ‘pillada’ sin respuesta (ni positiva ni negativa). El servicio A, tras esperar el tiempo determinado en el timeout, asume que se ha producido un error y finaliza la petición con un error de timeout.
Hemos visto los 4 patrones que consideramos los más esenciales cuando trabajamos con servicios distribuidos, son aplicables en muchos de los proyectos que nos encontramos en nuestro día a día.
Existen muchísimos más, algunos orientados a arquitecturas específicas o situaciones concretas cuyo encaje puede ser más optimo. Debemos tenerlos siempre listos y aprender de todos ellos y escoger siempre el más adecuado en función del caso de uso.