Patrones Cloud: Caché
Si recordáis mi último post, planteamos un problema: teníamos un servicio en una máquina que tardaba mucho, entre otras cosas por el acceso a la BBDD. Hoy veremos como solucionar eso usando una caché.
Aunque no sepáis nada de la Nube™ esta palabra os sonará, y es que la Nube™ no ha traído nada nuevo (ni siquiera ella misma, pero eso es otra discusión que tendrá que ser mantenida en otro momento). Las cachés se usan continuamente en informática para resolver problemas de acceso a datos como al que nos enfrentamos. El aparato que estés usando para leer esto tendrá una caché física, el sistema operativo que lo corra tendrá una caché virtual y el navegador con el que estés accediendo tendrá una caché de aplicación.
Pero centrémonos en nuestro problema y recordemos lo que teníamos entre manos:
Resulta que el servicio lo empieza a usar alguno más a parte de nuestros amigos, y estos usuarios que no te conocen son exigentes y se quejan de que el servicio tarda mucho en responder a cada llamada. Analizando los datos analíticos que tenemos vemos que hay dos operaciones que tardan de manera notable: una es la conversión de texto a imagen que a priori al ser algo que hace una librería de terceros no podremos mejorar; la otra parte donde el servicio tarda mucho es en la petición a la base de datos.
La query que le tiramos a la BBDD es muy complicada porque es una base datos legacy que cuando se montó estaba pensada para otra cosa y mirando el plan de ejecución no hay por dónde meterle mano. También vemos que lo que se trae de la base de datos es muy poquita información y que al final tampoco tenemos tantos usuarios aun.
Tenemos un problema concreto de velocidad, que en parte está ocasionado porque tardamos mucho en obtener unos datos que son fijos y no van a cambiar habitualmente.
Una manera de mitigar este problema es añadir un almacén en la memoria del servicio (por ejemplo un simple array) en el que buscar los credenciales antes de ir a buscar a la base de datos. El protocolo hasta ahora estaría siendo:
- El servicio recibe una llamada
- Pregunta a la BBDD por los credenciales del usuario
- Si los encuentra sigue con el proceso de conversión
- Si no los encuentra devuelve un error
Y añadiendo este almacén, pasaría a ser algo como lo siguiente:
- El servicio recibe una llamada
- Pregunta al almacén en memoria por los credenciales del usuario
- Si los encuentra sigue con el proceso de conversión
- Si no los encuentra
- Pregunta a la BBDD por los credenciales del usuario
- Si no los encuentra devuelve un error
- Si los encuentra
- Los añade al almacén en memoria
- Sigue con el proceso de conversión
- Pregunta a la BBDD por los credenciales del usuario
Con esto, a partir de la segunda llamada de un usuario, este verá mejorados los tiempos de respuesta y por tanto su satisfacción.
Es cierto que la primera llamada (que puede ser muy importante ya que mucha gente no da segundas oportunidades) aun estaría tardando demasiado. Esto se puede solucionar fácilmente metiendo una precarga de los datos cuando se arranque el servidor, o una carga en segundo plano que aproveche los tiempos de inactividad, de tal modo que para la mayoría de los casos cuando la petición llegue encontremos la información que queremos ya en memoria.
¿A que es una solución sencilla? Pues es una solución que se ha aplicado innumerables veces y se seguirá usando para solucionar problemas de acceso.
Ahora tenemos un servicio que responde en la mitad de tiempo y los usuarios están contentos ante la mejora y el haberse sentido escuchados. Tanto es así que te animas a añadir un sistema de pago y una de las cosas que compruebas en esa validación de credenciales es si tiene crédito, has añadido los datos necesarios en el perfil y un proceso que lo modifica en la base de datos y en la caché cuando el usuario compra crédito y lo reduces cuando hace una conversión. Son bastantes más datos los que tienes que mantener en memoria, pero vale la pena porque te estás ganando unos eurillos para cervezas y la solución sigue funcionando porque lo has tenido todo en cuenta… ¿seguro?
Parte de los eurillos en vez de cerveza has elegido reinvertirlos en publicidad para echar a andar la máquina de hacer dinero, y la publi está funcionando, trae nuevos usuarios y el servicio crece, pero de repente empiezan a sonar todas las alarmas porque el servicio está caído. Tienes a usuarios mandándote mails, increpándote en Twitter, amenazándote y pidiendo que les devuelvas el dinero ¡un horror!
Respiras hondo, bloqueas todas las notificaciones y te concentras en analizar el problema analizando los datos. Aparentemente el aumento de usuarios con las necesidades de memoria incrementadas por las nuevas estructuras para la gestión del cobro provocan que la memoria se pete. Te planteas cambiar el array en el que guardas todos por una cola FIFO pero visto el uso que hacen los usuarios y el número de registros que podrías mantener en memoria, estarías siempre consultando a la BBDD otra vez. Como tienes dinero y quieres resolverlo cuanto antes decides hacer un escalado vertical y pasar a un servidor de más tamaño, ¡estás en la Nube™! Haces los cuatro clicks de rigor y mientras se actualiza todo das gracias a Deity por haber elegido montarlo en la gran Nube™ en lugar de en tu ordenador de casa.
Todo está funcionando y aliviado das unos avisos en Twitter y mandas un correo a todos tus usuarios para decirles que eres un crack y que ya está todo rulando, que les das un par de créditos de regalo pero que de devolverles el dinero nasti de plasti.
Respiras tranquilo, cuando de repente todo vuelve a empezar, gente gritando, diciendo que haces mierda ¿qué pasa? Vuelves a tus datos y parece que todo funciona, no hay nada raro e incluso dirías que a tu máquina le queda mucho para tener problemas. Sin embargo, cuando intentas probar el servicio ves que no llegas a Infinitext. Dándole vueltas, hablando con expertos y amigos y leyendo un poco de literatura parece ser que tu proveedor de la Nube™ tiene el mismo canal de comunicaciones en todos los tipos de máquina y que en cuanto solucionaste lo del límite de memoria superaste el límite de conexiones que podía manejar tu sistema. El escalado vertical ha sido inútil, tienes que hacer un escalado horizontal, meter más máquinas pero ¿que pasará con la caché y el sistema de cobro tan chulo que habías montado?
Ya sabéis lo que toca, dadle vueltas a la cabeza hasta que volvamos sobre ello. Por el momento no os gastéis todos los eurillos en birra por si acaso 😉
Trackbacks/Pingbacks
[…] con esa serie que ya lleva una y dos entregas de soluciones de común aplicación en cualquier entorno pero especialmente en los […]
[…] los protocolos gossip vamos a dar por interrumpida esta serie, en la que hemos hablado de cachés, de particionado y de tablas hash siempre en torno a un ejemplo de Infinitext, un caso realista […]
[…] esta serie ya hemos hablado de las cachés, del particionado, y hoy vamos a hablar de las tablas […]