Cómo escribir código para software de alta disponibilidad

Existen servicios críticos que nunca deben dejar de funcionar (aeropuertos, sistemas sanitarios, etc). Para asegurar su disponibilidad, podemos poner en práctica algunos consejos.

Tenía en mis marcadores un interesante artículo publicado por Jacob Greenleaf, uno de los ingenieros de Imgur, con consejos sobre cómo escribir código para software de alta disponibilidad.

El software de alta disponibilidad es software que por nada del mundo puede dejar de funcionar. Este software se caracteriza por poseer un control de errores potente para evitar que la aplicación colapse; es por ello que la fase de tests es de una importancia tremenda para contener cualquier tipo de fallo. Este tipo de software lo podemos encontrar en todos los servicios en tiempo real (como Google, Twitter, etc.) o servicios críticos (software de aeropuertos, sistemas sanitarios, etc.)

Jacob nos da algunas pautas para tenerlo todo en cuenta y que nuestro sistema sea más robusto. Vamos a ello.

1. Poner límites a todo

Establecer límites a las tareas que realicemos es una práctica que no suele realizarse, pero las que nos dejan “esperando” por un resultado que nunca llega podrían ocasionar bloqueos inesperados en nuestra aplicación.

Tiempos de espera máximo para las peticiones de red, o un establecer un máximo cuando ejecutamos procesamiento de objetos marcan la diferencia y evitan que nuestros servidores (o nuestros clientes) queden bloqueados indefinidamente y sin información sobre el procesamiento.

En Imgur han creado una tarea que se llama “long query killer”. Esta tarea se encarga de escanear las peticiones a MySQL que se están ejecutando y comprueba el tiempo que estas llevan ejecutándose. Si superan un tiempo máximo, automáticamente la petición se cancela y se informa al cliente. Es una forma muy efectiva de que sea el propio sistema quien corte por lo sano y nos evite bloqueos.

2. Es bueno reintentar, pero exponencialmente

Cuando una petición falla (ya sea por disponibilidad del servicio, o por otro motivo), es una buena práctica intentar ejecutarla de nuevo. El problema surge cuando estos reintentos se ejecutan uno tras otro, ocasionando que nos podamos hacer un DDoS a nosotros mismos.

Lo ideal es que estos reintentos sean espaciados por periodos de tiempo cada vez superiores. Por ejemplo: reintentar dentro de un segundo, si vuelve a fallar reintentar dentro de 5, y así sucesivamente. Aunque no debemos olvidar el primer consejo; poner un tope máximo de intentos también es muy buena idea.

3. Usar supervisores y watchdogs

Cada proceso y tarea debe estar estructurada de tal manera que pueda ser lanzada por un gestor de tareas. Este gestor de tareas será el encargado de comprobar tiempos de ejecución, devolver resultados, parar, pausar o arrancar tareas. Si una tarea termina de manera inesperada, será este el encargado de reiniciarla desde un estado bueno y conocido.

Existen alternativas, como Monit, Pm2 o Forever, que se encargan de reiniciar el proceso del servidor si este falla.

4. Añadir comprobaciones de “salud”

Seguro que conoces las típicas páginas de “status” de las APIs y servicios más famosos de la red. Skype tiene Heartbeat, Facebook también la tiene o Github con su System Status nos proporcionan información sobre el estado de la plataforma, tiempos de respuesta y demás información útil para saber si el servicio está funcionando correctamente.

Si lo tienen ellos, ¿por qué tú no? Existen servicios (Amazon tiene algunos) que permiten saber de un vistazo si todo está funcionando como debe, si alguna instancia está caída, etc.

5. La redundancia es una obligación, no una opción

Y cuando hablamos de redundancia no nos referimos a duplicidad del código, sino a duplicidad de servicios e instancias. Si tienes un servicio funcionando en tu super instancia de AWS y por algún motivo esta cae, debes tener algún plan para levantar clones sin que esto afecte a la disponibilidad del servicio más que un par de minutos. Por ejemplo Amazon Web Services y da la posibilidad de establecer alarmas cuando ocurran ciertos eventos, y ejecutar algún tipo de proceso según nuestra conveniencia.

Con estos útiles consejos seguro que nuestras aplicaciones, aunque no sean de alta disponibilidad, mejoran en robustez ante errores.

test