Algunas veces los que codificamos olvidamos que nuestros programas tendrán que ejecutarse tarde o temprano sobre máquinas reales (o virtuales). Al margen de los tradicionales roces entre la gente de desarrollo y “los de sistemas”, se trata de un punto de riesgo que puede acentuarse en un entrono de desarrollo ágil.
Rockola.fm tiene un claro ejemplo de este conflicto en la implementación de la funcionalidad “Ahora en Rockola.fm están escuchando”. Se trata de una lista que aparece al final de nuestra home mostrando qué escuchan los seis últimos usuarios con actividad. Esta información se refresca cada cinco minutos.
Siguiendo el lema “mantenlo simple”, la primera implementación se limitaba a realizar una consulta sobre la BBDD cada vez que vencía un temporizador JavaScript. Asombrados por la rapidez del desarrollo (como media jornada) la nueva funcionalidad subió a producción en el siguiente pase. En esa época descorchábamos botellas de cava cada vez que superábamos los cien usuarios concurrentes. Nuestras infraestructuras estaban en consonancia con esa audiencia (una máquina para todo) y el riesgo de tener problemas de rendimiento era muy pequeño.
Sin embargo, con 1.000 usuarios concurrentes, esta petición se realiza casi dos veces por segundo que no te tira el servidor pero te lo ocupa de forma absurda.
Conscientes de que la BBDD sería uno de nuestros cuellos de botella (con “los de sistemas” identificando entradas sospechosas en el mysql-slow.log), afrontamos nuestra segunda refactorización incluyendo una gestión de cacheo de contenidos. El resultado fue espectacular hasta el punto que en algunos momentos las graficas para los servidores de BBDD (dos en balanceo por entonces) daban “respuesta plana”.
La gestión de cache complica el desarrollo pero la codificamos una sola vez en las clases base de nuestro patrón MVC. Para usarla basta implementar dos funciones virtuales que informan sobre la localización física de fichero y la duración del mismo. El cambio se limito a copiar el antiguo código sobre las nuevas clases y definir cinco minutos como duración del caché.
Sin embargo, con unos 5.000 usuarios concurrentes, el vencimiento de la caché ocurre símultáneamente para entre 20 y 50 usuarios que lanzaban a la vez una consulta bastante pesada generando picos innecesarios en la BBDD.
En esta ocasión, resolvimos el problema añadiendo un valor aleatorio al tiempo de vencimiento de la cache de modo que la función virtual para gestionar esto devolvía algo así como 300+rnd(50). De esta forma, limitamos las peticiones de refresco de caché entre 1 y 5 simultaneas. Una idea tonta pero efectiva y que seguro que empieza a dar problemas con 10,000 usuarios simultáneos.
Actualmente el “Ahora en Rockola.fm están escuchando” se genera mediante un proceso planificado lanzado cada cinco minutos. Muchas vueltas para llegar a la solución mejor y también la más sencilla.