EF: Cache de consultas

Nota: La información aquí presente aplica a Entity Framework 6, en versiones anteriores el comportamiento puede ser diferente.

 

Uno de los elementos que más desapercibido suele pasar de todos los elementos de Entity Framework es su sistema de cache de consultas. Cada vez que escribimos una consulta en LINQ, ESQL o realizamos una operación con nuestro contexto de trabajo, aplica también a operaciones de mantenimiento, Entity Framework utiliza una cache de la compilación de los diferentes Command Tree que son ejecutados. Aunque en un principio la compilación de estos CQT no debería llevar mas de unas pocas milésimas de segundo, en ocasiones, este tiempo puede ser muy alto, dependiendo de lo complejo de nuestras consultas, por lo que mantener una cache de estas estructuras es un aporte de rendimiento importante. A lo largo de este post trataremos de ver como funciona internamente este sistema de cache con el fin de que podamos aprender un poco más de Entity Framework.

 

QueryCacheManager

 

QueryCacheManager es la clase encargada de realizar nuestra cache de consultas, en realidad, las entradas pueden ser de otra naturaleza, pero para el post, lo simplificaremos así. Esta cache está limitada en su número de elementos, en concreto a 1000 entradas, por lo tanto, podríamos decir que a lo sumo una solución solamente podría tener para cada tipo de contexto de trabajo, fíjese que no digo instancia, 1000 entradas cacheadas. En realidad, este número difícilmente se va a llegar a conseguir puesto que nuestra clase de cache tiene un proceso de eviction que limpiará estos elementos. El desahucio de entradas se hace mediante un simple temporizador que se ejecuta cada minuto y comprueba si se deben de retirar entradas de cache, con una técnica muy simple y efectiva que veremos a continuación.

 

El desahucio

 

Para que Entity Framework libere entradas de nuestra cache el sistema tiene que superar un limite de entradas, que está establecido al 80 %, es decir, 800 entradas. Cada vez que nuestro temporizador de eviction salta, comprueba si el número de entradas es superior a 800. En caso afirmativo comienza la limpieza basándose en una propiedad, HitCount, que nos da una idea de las veces que esas entradas han sido utilizadas. Cada vez que se comprueba si una consulta está o no en la cache, esta incrementa el valor de la propiedad anterior, por lo tanto las consultas más utilizadas tienen unos valores de HitCount más altos. El proceso de eviction libera de la cache todas las entradas con un HitCount igual a 0, pero ¿como llegan las entradas a tener un hitcount igual a cero? La respuesta se debe a que a mayores de eliminar las entradas con hitcount cero el proceso de eviction se dedica en cada pasada a envejecer las entradas, con un simple right-shift con factores 1,1,2,4,8,16. Este envejecimiento hace que siempre que estemos en entornos de alto uso de cache, más de 800 entradas, los elementos menos utilizados irán desapareciendo.

Aunque el número de entradas, 1000 elementos, no es algo seleccionado al azar, los diferentes tipos de soluciones con las que nos podemos enfrentar hace que este número no siempre pueda ser un número justo, modificarlo con el fin de aumentarlo podría introducir mejoras en el comportamiento de una solución en producción. Por desgracia, por ahora, este número es fijo y no disponemos de ninguna forma para parametrizarlo  y observar resultados. Lo mismo, en realidad, podríamos decir del valor de carga máxima de la cache, el 80 % como comentábamos, o del tiempo de pasada del proceso de eviction.

 

Espero que os ayude y se entienda un poco mejor como funcionan ciertas partes de EF.

 

 

Saludos

Unai

EF 6.1 [Preview] : Carga automática de convenciones

Otra pequeñita entrada para mostrar otro método de utilidad que tendremos en EF 6.1, versión que pronto estará con nosotros. En este caso nos ocuparemos de la carga de convenciones, que como sabéis hasta ahora solamente se podía realizar mediante los métodos Add, AddAfter y AddBefore. Puesto que puede ser habitual la creación de un número significante de convenciones el incluir estas llamadas en nuestro OnModelCreating puede resultar en “código repetitivo”. Para simplificar esta tarea, ConventionsConfiguration dispone ahora de un método AddFromAssembly que nos permite cargar de forma automática todas las convenciones que tengamos en un determinado ensamblado. Concretamente, el método creado es:

Es importante notar que como la ejecución de las convenciones se hace en orden de registro la carga automática de convenciones de un ensamblado impone un orden implícito basado en el nombre del tipo que representa la convención como se puede observar con el uso del método extensor OrderBy.

 

La selección de convenciones válidas se hace mediante el uso de un pequeño filtro, tal cual el siguiente, que nos permite registrar convenciones de modelo, almacén y de configuración.

 

El pull request asociado a esta característica en la que podéis ver todos los cambios necesarios es este. Espero que os resulte interesante y sobre todo útil, puesto que para esto se ha hecho!!

 

saludos

Unai