Cacheitis: Lo mínimo que todo desarrollador debe saber sobre las cachés

Creo que, después de tanto artículo sobre Scrum, los lectores de mi blog agradecerán una entrada que no tenga nada que ver con las metodologías ágiles. Sí, sí, yo también estoy un poco saturado del tema, pero de todos modos, no dudéis que vamos a seguir hablando de ello en este blog. Pero antes de meterme en harina, permitidme que os presente www.scrumweek.com, una semana enterita de formación sobre metodologías ágiles.

Pero como decía hoy no vamos a hablar de metodologías, sino de arquitectura y diseño de software. De hecho vamos a hablar de uno de los elementos claves de toda arquitectura: la caché.

Seguro que ninguno de los lectores ignora los beneficios que una caché bien diseñada proporciona a casi cualquier aplicación. Se puede asegurar que sea cual sea el dominio de nuestra aplicación el rendimiento de la misma se verá beneficiado por el principio general de almacenar datos en lugares donde su acceso sea más rápido (memoria frente a disco, memoria frente a base de datos, capas más cercanas de la aplicación frente a capas más lejanas…).

Este principio general se cumple casi siempre, pero no es el motivo de este post hablar de los ya conocidos efectos beneficiosos del cacheo, sino de los usos aberrantes que a menudo he visto de la cache. Y es que, si bien una cache bien utilizada es un catalizar claro del buen rendimiento, una mal uso de la cache es más a menudo de lo que muchos desarrolladores sospechan fuente de serios problemas de rendimiento. Es la tan temida cacheitis. La cacheitis es una enfermedad que sufren muchísimas aplicaciones, consiste en hacer una mal uso de la cache que provoca una dolorosa inflamación de la misma que la hace totalmente inservible. La cacheitis se manifiestas con muchos síntomas diferentes siendo los más significativos un altísimo consumo de memoria y un mal rendimiento de la aplicación.

La cacheitis tiene sus orígenes en diversos malos usos de la caché casi siempre relacionados con olvidar alguna de las siguiente buenas prácticas:

Asume que no puedes cachearlo todo: Como arquitecto, cuando te enfrentas a la difícil decisión de diseñar tu modelo de caché debes tener en cuenta que no puedes cachear todo. La cache por definición es finita. Si puedes contar con una caché infinita, simplemente no necesitas una caché. El lugar en el que suelen acabar todo los datos es la memoria, si todos los datos que va a manejar tu aplicación caben en memoria por definición y tienes la seguridad de que esto siempre va a ser así, simplemente almacénalos directamente en memoria (ignoro aquí la necesidad de persistencia). Por ejemplo supongamos una aplicación que utiliza una tablas de cálculo, cargar directamente esas tablas en memoria puede ser una opción interesante. De todos modos, hemos de reconocer, que a menudo las aplicaciones no puede asumir está premisa. En ese caso es una labor sumamente difícil decidir cual es el balance justo entre consumo de memoria y uso de recursos más lentos como acceso a disco y petición de datos por la red, pero ese no es le punto ahora. El punto ahora es que la memoria es finita y además un recurso compartido y por tanto tu cache tiene que estar explícitamente limitada. Diseñar si tener en cuenta que la cache es finita y cachearlo todo es un error de base, una cache que cachea todo nunca va a ser efectiva. Una frase que en el Debugging and Optmization Team de Plain Concepts hemos oído a menudo es: ‘Cómo puede ser lenta la aplicación si lo cacheamos todo’… cachear todo es dañino por que la cache es finita. Si en una cache finita, cacheas todo el resultado será que algo tendrá que salir de la caché para que entre lo último que has cacheado y que tu cache por lo tanto siempre contendrá lo último cacheado, pero no lo que más importante sea cachear. La cache debe almacenar lo recursos costosos que se acceden con más frecuencia, no los últimos accedidos.

El cacheo temprano es el origen de todos los males: Parafraseando la mítica frase de Donald Knuth , ‘la optimización temprana es el origen de todos los males’, tomar decisiones de cacheo demasiado temprano es dañino en la mayoría de las ocasiones. A menudo cuando diseñamos una arquitectura establecemos mecanismos de cacheo que luego son utilizados hasta el abuso. El peligro subyacente al cacheo temprano es que comencemos a cachear sistemáticamente elementos que nos parecen costosos sin tener evidencia de que realmente son los mejores candidatos para su cacheo. Por ejemplo, llegamos a una historia de usuario que requiere cargar datos de provincias y como suponemos que van a cambiar poco decidimos cachearlos ignorando que el factor más importante a la hora de cachear es el coste de obtener la información multiplicado por el número de accesos que esa información va a ser accedida. Cachear toda la información que cambia poco simplemente por que es fácil de cachear es un error habitual. Además los patrones de acceso a la información en aplicaciones complejas son difícilmente predecibles, es muy difícil sin analizar el rendimiento de tu aplicación mediante unas adecuadas pruebas de carga que modelen el uso que va a tener, tomar las decisiones correctas sobre que cachear. Diseña para hacer el cacheo posible, pero no decidas que cachear hasta que no te quede más remedio.

No solo existe una caché: Otro error habitual es olvidar que no solo existe una cache en el sistema. El gestor de bases de datos que utilizamos implementa mecanismos de cache, el servidor web que utilizamos implementa mecanismos de cache, nuestra aplicación implementa mecanismos de caché… ¡y todos usan la memoria!. Hace un tiempo nos llamaron para ver que estaba ocurriendo en una aplicación que hacia un buen uso de la caché, de hecho el comportamiento de la aplicación era adecuado la gran mayoría de las ocasiones… excepto cuando el ‘servidor’ tenía menos de dos bigas de memoria situación en la que todo se volvía escandalosamente lento. Lo más sorprendente es que en la última versión de la aplicación habían añadido una caché que mejoraba mucho el rendimiento en la mayoría de los casos. Sin embargo la versión anterior sin caché se comportaba aceptablemente en servidores con poca memoria. Tras estudiar el comportamiento de la aplicación, sorprendentemente, en situaciones con poca memoria, elementos costosos que habitualmente debían estar en la caché sistemáticamente tenían que ser recuperados desde la base de datos. Y lo que es peor, cuando observábamos el comportamiento de la base de datos ¡también veíamos tiempos de respuesta muy elevados!. Tras tirar del hilo nos dimos cuenta de lo que estaba ocurriendo: SQL Server y ASP.Net/IIS se estaban pegando por la memoria para colocar sus respectivas cachés. Vaya, nos habíamos olvidado de que ¡la memoria es finita!. La solución, sencilla, limitar la memoria máxima a utilizar por parte de SQL Server y de ASP.Net y listo… problema solucionado.

Una colección estática o global no es una caché: Aunque parezca sorprendente, no es extraño encontrar desarrolladores que implementa su propio sistema de caché. Esto siempre es una aberración. Hoy por hoy todas las tecnologías que podemos usar en una arquitectura .Net en las que el cacheo puede jugar un papel tiene un mecanismo de caché. ¡He visto cachés implementadas como colecciones estáticas! Una mecanismo de caché que no cuenta con un mecanismo de invalidación no es un mecanismo de caché. Tan importante es mantener en caché los elementos que se acceden con más frecuencia como liberar la memoria utilizada por aquellos elementos en cache que no son accedidos. Igualmente es importante, siempre que se decide cachear un elemento establecer la política de invalidación de dicho elemento.

La cacheítis, como cualquier enfermedad tiene síntomas que nos pueden poner sobre su pista. Casi todos estos síntomas se manifiestan con contadores de rendimiento fuera de control. El el Performance Monitor (Perfmon.exe) el termómetro que nos indicará que podemos estar sufriendo de cacheitis. Hay un parámetro que todo mecanismo de caché suele publicar en sus contadores rendimiento y que es fundamental a la hora de diagnosticar una cacheítis:

Cache Hit Ratio:

Cuando el cliente de un sistema de caché pide el acceso a un dato que es susceptible de encontrarse en la caché y lo encuentra se produce un ‘hit’ de la cache. La relación entre las veces que se hacen peticiones a la cache y las veces que el dato buscado en la caché se encuentra en esta es el Cache Hit Ratio. Un porcentaje bajo en este indicador es un síntoma claro de un mal uso de la caché. Se suele considerar un valor adecuado un ratio de alrededor del 90%.

En la siguiente imagen se pueden ver diferentes contadores de rendimiento de componentes típicos de una aplicación .net:

image

¡Ojo con la cacheitis!

¡ScrumWeek y el primer curso de certificación Professional Scrum Developer en España!

Scrumweek2011_r2_c1

En el marco de la primera edición de la ScrumWeek organizada por Plain Concepts y Proyectalis, desde Plain Concepts hemos preparado el primer curso de certificación Professiona Scrum Developer en España. Pero no solo podrás aprender y certificarte sobre Scrum y TFS a fondo, además hay formación sobre: Pruebas unitarias, Arquitectura ágil, Coaching de equipos ágiles, Scrum (de manera agnóstica a la tecnología) y sesiones gratuitas.

Regístrate ahora con un descuento del 50% sobre el valor del curso.

El programa Professional Scrum Developer .NET (PSD.NET) capacita, evalúa y certifica a los desarrolladores de Scrum que trabajan en tecnologías .NET. Durante Scrumweek se llevará a cabo un curso PSD.NET durante el cual se mostrará cómo utilizar prácticas de ingeniería de software modernas para desarrollar un incremento de funcionalidad potencialmente entregable, en el marco de Scrum, Visual Studio y Team Foundation Server 2010, y trabajando como parte de un equipo autoorganizado y multifuncional que practica un desarrollo iterativo e incremental. Las clases son guiadas por ejercicios prácticos, en los que los estudiantes trabajan en equipo para desarrollar incrementos completados de elementos del Backlog de producto.

Lugar y fecha

La ScrumWeek Madrid 2011 tendrá lugar del 4 al 8 de Abril (lugar pendiente de determinar).

PROGRAMA

El programa de esta semana Ágil incluye:

Cursos

  • Curso de Scrum (iniciación a intermedio), por Rodrigo Corral y José Luis Soria (lunes-martes, 9 a 18)
  • Coaching de equipos Ágiles, por Ángel Medinilla (lunes-miércoles, 9 a 15)
  • Arquitectura Ágil, por Unai Zorrilla (miércoles-jueves, 9 a 15)

Seminarios y talleres en profundidad

  • Gerencia Ágil, por Ángel Medinilla (jueves, 9 a 18)
  • Taller de pruebas unitarias, por Ibón Landa (viernes, 9 a 15)

Sesiones divulgativas gratuitas

  • Visual Studio Ágil, por Ibón Landa (jueves-viernes, 16 a 18)
  • Cultura Corporativa Ágil (lunes-martes, de 16 a 18)

Scrum Clinic

Por Ángel Medinilla y Rodrigo Corral (viernes, 9 a 15)

Curso oficial Professional Scrum Developer .NET

Por Rodrigo Corral y José Luis Soria (lunes-viernes, día completo)

Adicionalmente, el programa incluirá la celebración de una serie de sesiones divulgativas, talleres, coloquios, reuniones de grupos locales y networking, además de un Coding Dojo, las tardes de lunes a miércoles, así como un barcamp-cena el jueves. Permaneced atentos a esta página y a nuestras noticias para más información.

Scrumweek2011_r6_c2