Entrevista en Comando Tomate

Otra desternillante pero útil entrevista en comando tomate. Para los que no me conozcan cuento un poco lo que hago en la Universidad y para PlainConcepts. Además cuento alguna anédocta y consejos del «tomate» para los desarrolladores 🙂


 En breve publicaran otra entrevista más técnica sobre Spring.Net


Originalmente publicada aquí: http://comandotomate.net/archive/2008/05/07/entrevista-a-eduardo-quint-225-s.aspx


 

Evento .NUGG: IIS7.0 en A Coruña

Tras colaborar en el Visual Studio 2008 Tour, el grupo .Nugg (.Net User Group Galicia) tiene nuevo evento:


IIS 7.0: Hágase la luz


El 16 de Mayo de 17:00 a 19:30, Iván González, MVP de IIS, nos va a contar las novedades de Internet Information Services 7.0 en Windows Server 2008 y Windows Vista.


El evento es, como todos los de .Nugg en A Coruña, en el edificio Xoana Capdevielle dentro del campus de Elviña (Universidade da Coruña).


Es necesario que os registreis.. que luego no hay sitio :p


http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032378922&Culture=es-ES


Un mapita de cómo llegar, especialmente para la gente de fuera


Ver mapa más grande

nHibernate en Comando Tomate

Os presento una entrevista que me hizo Elisa para el Comando Tomate, que es un espacio web rojo con una misión tecnológicamente tomatera, verde y madura. Que además de entretener, educa :p
Atentos a mi destreza en las manos… lo aprendí de los mejores políticos :p  Además hablo de nHibernate.
Mi frase favorita, de esas que le salen a uno porque se quiere a si mismo (jejeje) :
«Yo conozco SQL y le (por nhibernate) he visto sacar consultas mucho mejor CASI de las que podría hacer yo»
Eduardo Quintás en Comando Tomate.

Materiales de la charla: ADO.NET 3.0 Entity Framework

Tal como os había prometido a los que acudisteis a la charla os publico (un poco tarde, I know) los materiales de la misma.


Tenéis aquí la presentación de ADO.NET 3.0 Entity Framework Beta3 (está en pptx, 337KB) y también el código fuente de lo realizado en la charla (en zip 40KB).


Para el código fuente es necesaria la base de datos MyPeople.mdf que puede encontrase en el GettingStarted de EF BETA 3 en codeplex:
http://www.codeplex.com/Project/Download/FileDownload.aspx?ProjectName=adonetsamples&DownloadId=23191

ADO.NET Entity Framework: Reattaching detached objects in short Lived contexts

Most enterprise applications uses an architectural pattern that needs a stateless facade for its business logic. So they can use it in several scenarios like WebServices or WCF, ASP.NET applications, Traditional Desktop Application (Windows Forms, WPF).. etc.


If you’re using Entinty Framework, your derived ObjectContext is called short lived context and there is some issues about it. My last post (in spannish language) explores best practices for better perfomance with short lived contexts in Entity Framework.


In this post I speak about re-attach modified EntityObjects, that were updated outside of an ObjectContext (in detached state). This is a very common scenario when you’re using stateless facades.


So, imagine that your facade retrieves an EntityObject of your model and passes it to some visual control in the user interface. When an user or a process in IU changes your EntityObject, this object is in a detached state, so there isn’t an ObjectStateManager tracking changes. When your passes it again to the facade at your Business Logic Layer this object needs to be persisted. Problem is that the new ObjectContext doesn’t know about changes in properties, so when you call SaveChanges() nothing happens.


At User Interface layer:


MyEntity entity = Facade.GetEntityById(1); 
BindEntitiesToControls();


(User interaction that changes values in the detached object, by example a roundtrip in an ASP.NET app. The entity object could be in the viewstate or HttpSession) 


entity.PropertyValue = 4; 


(User clicks «Save» button)


Facade.Save(entity); 


At Business Layer Facade (naive aproximation):


public static void Save(EntityObject entity)
{
   using(MyContext ctx = new MyContext)
   {
     ctx.Attach(entity);
     ctx.SaveChanges();
   } 
}


This aprox. does NOT work!!! because the ObjectContext unknowns the changes ocurred in entity object when it was detached.


Upsss, but this is a very common scenario, so what can i do?


Take a look to this extensor method for an ObjectContext:


/// <summary>
/// Attach an EntityObject that was modified when detached
/// </summary>
/// <param name=»obj»></param>
/// <param name=»objectDetached»>DetachedObject</param>

public static void AttachUpdated(this ObjectContext obj, EntityObject objectDetached)
{
  
if (objectDetached.EntityState == EntityState.Detached)
  
{
     
object original = null;
     
if (obj.TryGetObjectByKey(objectDetached.EntityKey, out original))
         
obj.ApplyPropertyChanges(objectDetached.EntityKey.EntitySetName, objectDetached);
      
else
      
throw new ObjectNotFoundException();
   
}
}

This extensor method first tries to load original entity into the object cache. If it’s already loaded, nothing happens; if not, it is retrieved from the storage.
Then applies the objectDetached properties to the original object retrieved in last step.


Our new facade method could be:


public static void Save(EntityObject entity)
{
   using(MyContext ctx = new MyContext)
   {
     ctx.AttachUpdated(entity);
     ctx.SaveChanges();
   } 
}
 

ADO.NET Entity Framework: Buenas prácticas para contextos de tiempo de vida cortos

En este post comento una serie  buenas prácticas para mejorar la eficiencia de ADO.NET Entity Framework (EF) en escenarios de aplicaciones empresariales dónde es recomendable utilizar contextos de tiempo de vida cortos (Short Lived Contexts)


AVISO: Lo aquí expuesto es aplicable a ADO.NET Entity Framework Beta 3. Es posible que en versiones posteriores esta información no sea correcta.


Introducción


Los principales patrones arquitectónicos para aplicaciones empresariales emplean una fachada que expone al UI o UIC la lógica de negocio de la misma. Esta fachada idealmente no debe mantener el estado (stateless) para que, entre otras razones, pueda utilizarse desde aplicaciones web como ASP.NET, servicios web,  WCF o clientes inteligentes ligeros en forma de aplicación de escritorio tradicional.


Si empleamos EF en nuestra capa de negocio tendremos métodos  que crean un ObjectContext local al inicio de su ejecución y liberan al final del método tras realizar una serie de operaciones de lógica de negocio mayormente breves. Se dice que estos ObjectContext son Short Lived  o de tiempo de vida corto.


Por el contrario, los contextos de tiempo de vida largo (long lived contexts), suelen estar asociados a aplicaciones de escritorio o consola tradicionales y se crean y destruyen al inicio y fin de la aplicación. Si bien también se aplica cada vez más el patrón arquitectónico anteriormente descrito en estas aplicaciones empresariales de escritorio. Así es posible distribuir la lógica de negocio y que la aplicación empresarial  con interface de escritorio (WPF, WindowsForms, Consola…) se comporte como un cliente inteligente ligero. Además podríamos aprovechar dicha lógica de negocio para ser usada con aplicaciones con interfaz web o desde servicios web o incluso WCF.


Los ORMs (Object Relational Mapping) como Entity Framework o NHibernate se emplean principalmente en estos escenarios empresariales y es curioso que la mayoría de la literatura y documentación al respecto inciden en presentar sus características en entornos con contextos de tiempo de vida largo y apenas se encuentran referencia a los Short Lived Context cuando son prácticamente el escenario arquitectónico empresarial más común.


Una de las ventajas de los ORMs es que nos dan una indirección estupenda del almacén final de datos y su consulta específica. Pero también nos añade el desconocimiento de cómo trabaja internamente y si el código que escribimos es o no eficiente. Para crear aplicaciones empresariales eficientes, algo imprescindible en entornos empresariales con alto número de peticiones, es preciso conocer cómo trabaja internamente el ORM que empleamos y el Entity Framework no es una excepción. El principal desafío es que pocos tenemos el tiempo suficiente para investigar su funcionamiento y deducir buenas prácticas al respecto.


Gracias a dos artículos publicados en ADO.NET TEAM Blog sobre rendimiento del Entity Framework [1] [2]y la fantástica FAQ de Entity Framework [3] recopilada por Danny Simmons en su blog Sys.Data.Objects dev guy se pueden extraer algunas buenas prácticas para mejorar el rendimiento en Entity Framework, especialmente para aquellos entornos con contextos de tiempo de vida cortos.


Buenas prácticas para mejorar la eficiencia en escenarios con Short Lived Contexts


Vamos a centrarnos en los siguientes aspectos:


·         Acelerar la creación de ObjectContexts


·         Afinar el ObjectStateManager


·         Optimizar la ejecución de consultas en LINQ to Entities


Acelerando la creación de ObjectContext


Uno de los principales problemas a lo que nos enfrentamos es que crear un ObjectContext es una actividad costosa, especialmente la primera vez. En una aplicación con short lived context se suele crear y desechar un ObjectContext prácticamente por petición a la lógica de negocio. En una sesión o en un petición desde el interface es fácil tener una o más invocaciones a la fachada de la lógica de negocio que utilizan varios ObjectContexts. Si multiplicamos estas instanciaciones por peticiones concurrentes, el número de ObjectContext creado se dispara. El EF proporciona alguna optimización al respecto [1]; la primera vez que se crea un ObjectContext se cachea la información de metadata del modelo que se construye a partir de los archivos CSDL, MSL y SSDL. Esta información se cachea y se comparte a nivel de dominio de aplicación (38% del tiempo). Otra gran parte del tiempo de esta primera instanciación se emplea en la creación de la vista abstracta de la base de datos en memoria (56% del tiempo), que también se cachea.  Solo el 7% del tiempo se emplea en la materialización (por ejemplo un DBReader obteniendo los datos físicos)


En la práctica tenemos que una primera ejecución de un método con una consulta a través de EF es del orden de 300 veces más lenta que las consultas sucesivas. Las consultas en caliente son relativamente rápidas y el 74% del tiempo se emplea en la materialización de la consulta y solo el 4,13% en la creación del ObjectContext.


Antipatrones


Si a alguien se le ocurre compartir un único ObjectContext por dominio de aplicación debe saber que no es una buena idea porque el EF no es thread Safe, como la gran mayoría de clases del framework, si bien podría implementarse un mecanismo de bloqueo propio. Ver [3]


Si estamos en una aplicación ASP.NET puede parecernos tentador guardar en la sesión el ObjectContext pero tampoco es una buena idea ya que el tamaño de la sesión crecería enormemente y su correcta serialización tampoco es trivial. Ver [3]


Buena práctica


La principal aproximación para rebajar el tiempo de creación del contexto es en su primera instanciación. Se puede reducir el tiempo de generación de la vista (un 58% del tiempo de creación) teniéndola compilada previamente.


Para ello deberemos ejecutar edmgen.exe con la opción /mode:ViewGeneration, esto crea un fichero de código que puede ser incluido en nuestro proyecto.


En las pruebas realizadas en [1] se obtuvo un descenso del 28% de tiempo en la primera instanciación. La desventaja  es que si hacemos cambios en los archivos de metada del EDM necesitaremos recompilar la aplicación. Pero si eso podemos asumirlo, deberíamos aplicar siempre esta optimización.


Afinando el ObjectStateManager


Cada ObjectContext tiene un ObjectStateManager que, entre otras tareas, mantiene el estado correcto de los objetos cacheados en memoria. Por defecto si se recuperan nuevas entidades del almacén de datos a través de una consulta, ESQL o LINQ  to Entities, EF solo cargará aquellas que no están aun en memoria, llevando un seguimiento efectivo de las entidades en memoria con respecto a su persistencia física en el almacén de datos.


Llevar este seguimiento o tracking es útil en aquellos contextos dónde realicemos varias consultas que impliquen un mismo conjunto de entidades.


Pero lo más normal es disponer de abundantes métodos en nuestra fachada que simplemente recuperan una enumeración de entidades para ser enlazadas a su vez a un control visual como un GridView o un DropDownList.


Buena práctica


Es posible modificar esta política de seguimiento del ObjectStateManager para un ObjectQuery concreto, desactivándola en casos como el anteriormente descrito, obteniendo una ligera mejora en los tiempos.  Un ObjectQuery tiene un atributo llamado MergeOption que es un enum con los valores: AppendOnly, NoTracking, OverwriteChanges, PreserveChanges. Con la opción MergeOption.NoTracking no se hacen comprobaciones de seguimiento.


Por ejemplo:

using (MyTravelPostEntities entities = new MyTravelPostEntities())
{
    entities.BlogPosts.MergeOption = MergeOption.NoTracking;
    IQueryable<BlogPost> postQuery = (from bp in entities.BlogPosts
                         where bp.Comments.Count() > 0
                         select bp);
    BlogPost post = postQuery.First();

De todas formas, según los tiempos obtenidos en los artículos referenciados, la comprobación de seguimiento por parte del ObjectStateManager  solo abarca de un 1% a un 3% del tiempo total de la consulta.


Optimizando las consultas en LINQ  to Entities


De la misma forma que se cachean los metadatos también se cachean las consultas a nivel de Entity Framework, por eso las segundas ejecuciones de la misma consulta son más rápidas. Al igual que el caché de metadatos, el caché de consultas tienen un ámbito global de dominio de aplicación.


Ojo, no confundir este caché de consultas de EF con el caché de planes de ejecución de consultas SQL en SQL Server u otro gestor de base de datos relacional.


Si bien una consulta en ESQL se cachea prácticamente toda, no ocurre lo mismo con una consulta en LINQ To Entities, algunas partes tienen que construirse de nuevo en las ejecuciones sucesivas; por ejemplo el árbol de expresión resultante tiene que ser calculado o compilado en memoria en tiempo de ejecución y para cada invocación de la consulta LINQ to Entities necesita ser reconstruido.


Lo ideal sería que estas consultas LINQ solo se tuvieran que compilar una vez.  Es posible hacerlo utilizando, por ejemplo, una función estática anónima[1] que actúe de delegado. Dando lugar a consultas compiladas LINQ; que son más rápidas que las consultas LINQ To Entities tradicionales siempre que se ejecuten con cierta frecuencia.


Buena práctica


Supongamos esta petición con contexto de tiempo de vida corto (obsérvese la aplicación de la buena práctica del apartado anterior):

using (PFCNetEntities ctx = new PFCNetEntities())
{
  ctx.Projects.MergeOption = ctx.MergeOption.NoTracking;
  IQueryable<ProjectRegister> query =
      from project in ctx.Projects
      where project.User.UserName == UserLogin
      select project;  pr = query.FirstOrDefault<ProjectRegister>();
} 

Para que no sea necesario volver a construir el árbol de expresión de la consulta LINQ to Entities utilizamos una expresion lambda1 estática que se ejecute al comienzo del tiempo de vida de nuestro dominio de aplicación, obsérvese que admite el paso del parámetro UserLogin para filtrar la consulta:

 

private static Func<PFCNetEntities, String, IQueryable<ProjectRegister>>
    compiledProjectRegisterByUserQuery = CompiledQuery.Compile(
        (PFCNetEntities ctx, string UserLogin) =>
             (from project in ctx.ProjectRegister
              where project.User.UserName == UserLogin
              select project)); 

public ProjectRegister GetProjectRegisterByUser(string UserLogin)
{
     ProjectRegister pr = null;
 
    using (PFCNetEntities ctx = new PFCNetEntities())
     {
        ctx.ProjectRegister.MergeOption = MergeOption.NoTracking;
        pr = compiledProjectRegisterByUserQuery(ctx,
                  UserLogin).FirstOrDefault<ProjectRegister>();
    }
    return pr;
}
 

En las pruebas realizadas en [2] se observa que para la primera ejecución de una consulta LINQ frente a una consulta LINQ Compilada se obtiene paradójicamente que la consulta LINQ es un 23% más rápida. Si bien en sucesivas ejecuciones (suponemos siempre que en nuestro escenario empresarial las consultas son invocadas frecuentemente) se obtiene que las consultas LINQ compiladas, que por ejemplo tienen un filtro, son del orden de un 80% más rápidas. Este dato es importante porque es una mejora significativa en los tiempos de ejecución. Siendo muy recomendable utilizar LINQ to Entities compiladas en aquellas consultas que sabemos a priori que van a tener una alta tasa de invocaciones.


Resumen


Los principales patrones arquitectónicos para aplicaciones empresariales implican el uso de contextos de tiempo de vida corto, especialmente en aquellas aplicaciones ASP.NET, servicios web o WCF. Se han presentado tres buenas prácticas para mejorar la eficiencia en este tipo de escenarios.


La primera reduce el tiempo de creación del primer ObjectContext en un dominio de aplicación.


La segunda desactiva la comprobación de seguimiento para el ObjectStateManager en aquellas operaciones de mera recuperación de datos.


La tercera y última buena práctica define un patrón para utilizar consultas LINQ To Entities compiladas.


Por último, este post está totalmente abierto y con vuestras aportaciones y lo que vaya surgiendo de los blogs de ADO.NET y System.Data.Object Dev Guy lo iré ampliando hasta tener un catálogo de buenas prácticas que mejoren la eficiencia de EF en escenarios comunes como los de contextos con tiempo de vida cortos.


Referencias

 











[1]

Exploring the Performance of the ADO.NET Entity Framework – Part 1
http://blogs.msdn.com/adonet/archive/2008/02/04/exploring-the-performance-of-the-ado-net-entity-framework-part-1.aspx
(04/02/2008) 

[2]

Exploring the Performance of the ADO.NET Entity Framework – Part 2 http://blogs.msdn.com/adonet/archive/2008/02/11/exploring-the-performance-of-the-ado-net-entity-framework-part-2.aspx
(11/02/2008)
 

[3]

EF FAQ updated – v0.3
http://blogs.msdn.com/dsimmons/archive/2008/01/04/ef-faq-updated-v0-3.aspx(04/02/2008) 
 






[1] para aquellos que no les suene, una función anónima es una expresión típica del lambda cálculo introducida en C# 3.0 y que ha mejorado la forma de definir delegados. Más información.

Eventos .NUGG en Febrero: ADO.NET Entity Framework y SQL Server 2008

Os anuncio la proximidad de dos interesantísimos 😉 eventos .NUGG (.Net User Group Galicia) en A Coruña.

Es necesario el registro previo.

 

Viernes 22 de Febrero de 17:00 a 19:30

.Net 3.5: ADO.NET Entity Framework

Eduardo Quintás


 


Viernes 29 de Febrero de 17:00 a 19:30

More Magic – SQL Server 2008

Pablo Álvarez Doval


 

Se celebran en los aularios del CUFIE en el edificio Xoana Capdevielle del campus de Elviña.

Aqui teneis su localización

 

Espero veros a alguno!

Entity Framework BETA 3

El mes pasado, el ADO.NET Team, tal como nos comentaba elbruno, publlicó una nueva release beta del ADO.NET Entity Framework (EF en adelante) y una nueva CPT de las herramientas visuales para la generación del modelo de datos de entidades (EDM).


Como sabeis el EF es el primer ORM de Microsoft para la tecnología ADO.NET y conociendo previamente NHIbernate, la verdad es que ofrece una característica que hara las delicias de los programadores: LINQ to Entities. Es posible aprovechar toda la potencia de LINQ para consultar las entidades de nuestro modelo de forma efectiva, rápida y homogenea, siguiendo el ideal de Microsoft de aprender un único lenguaje de consulta de datos (LINQ) para múltiples tipos de datos (colecciones en memoria, XML, registro de windows, etc.) que se compila (y por tanto se comprueban los tipos) y no se resuelve en tiempo de ejecución como los largos strings con consultas SQL o HQL en NHibernate que eran culpables de bastantes errores en tiempo de ejecución y que requerían un depurado exhaustivo y algo tedioso.


Si quereis probarlo necesitareis por un lado VisualStudio 2008, podeis descargaros un Trial desde aquí o también el VisualStudio 2008 Express que es gratuito y luego instalar el Entity Framework Beta 3. Después es necesario instalar el XML Editor QFE para finalmente proceder a la instalación del Entity Designer CTP2


También os va a hacer falta tener instalado SQL Server 2005 (vale la edición express) para poder ejecutar los ejemplos disponibles en codeplex.


Como comentaba Bruno mucha gente se pregunta cuándo utilizar EF y cuando LINQ to SQL. La principal diferencia, a mi modo de ver, es que LINQ to SQL solo trabaja contra SQL Server, algo a tener en cuenta pues EF ofrece más proveedores de datos. Actualmente y según un post reciente del ADO.NET Team Blog ya existen proveedores para Oracle, MySQL, PostgreSQL, SQLite, Informix, Ingres, Sybase, DB2, Progress, VistaDB, SQL Anywhere así como un bridge para drivers ODBC y JDBC. También se ha anunciado que pronto estará disponible un proveedor para Firebird. Ya en épocas tempranas al desarrollo de EF se ofreció un ADO.NET Sample Provider que ayudaba de desarrolladores de proveedores de acceso a datos implementar sus propios proveedores de EF. Tanto que en el TechED celebrado en Junio de 2007 en Orlando, Brent Goss del equipo de DB2 de IBM mostró un ejemplo con 101 consultas de LINQ to Entities sobre un SGBD DB2. 


A todos aquellos que esteis interesados en ORMs o trabajeis con ellos os animo a que echeis un ojo al EF y saqueis vuestras propias conclusiones… para mi es uno de los productos de desarrollo en acceso a datos más interesantes actualmente.

Artículo "Visualización de grandes conjuntos de datos en ASP.NET" publicado en DotNetManía: Correcciones y recursos.

En artículo publicado en DotNetManía de este mes, comentaba en un punto que « …  también se planteó la posibilidad de utilizar procedimientos almacenados, aprovechando que se compilan y su plan de ejecución nunca se descarta de un caché …«. Que aunque no afecta a la esencia del artículo, para nada es correcto. Los procedimientos almacenados en SQL Server no se compilan. Se compila su plan de ejecución y es tratado en la caché como un elemento más con las mismas politicas que por ejemplo una entrada marcada como prepared_query. Esta compilación se lleva acabo en la primera invocación del procedimiento almacenado y puede ser recompilado a lo largo del tiempo por diversos motivos. Si os interesa podeis consultar el post «Caché de planes de ejecución en SQL Server 2005 y comportamiento de los procedimientos almacenados» dónde explico con detalle preguntas lógicas sobre el rendimiento de los procedimientos almacenados con consultas y consultas marcadas como prepared_query.


Por otra parte a todos aquellos que esteis interesados en obtener el código fuente del artículo podeis descargarlo haciendo clic aquí

Caché de planes de ejecución en SQL Server 2005 y comportamiento de los procedimientos almacenados

Esta mañana Iván me preguntaba sobre una afirmación que ponía en las conclusiones de artículo «Visualización de grandes conjuntos de datos en ASP.NET» publicado en la DotNetMania de este mes. Decía « …  también se planteó la posibilidad de utilizar procedimientos almacenados, aprovechando que se compilan y su plan de ejecución nunca se descarta de un caché …«. Esto es totalmente incorrecto y quizas me deje llevar por mis viejas experiencias con Informix y 4GL. [:P]


Además nos empezaron a surgir bastantes dudas sobre el comportamiento de los planes de ejecución de los procedimientos almacenados.


¿Los Procedimientos almacenados se compilan? ¿cuándo lo hacen?
¿Hay alguna diferencia en la forma de tratar la caché de los planes de ejecución marcados como prepared_query o stored proc?
¿Cuál es el tamaño del caché de planes de ejecución?¿Cuál es la política para descartar elementos?
¿Cuándo se invalida el plan de caché, datos etc y se precisa de recompilación?


Documentación consultada


Primero miré algunos blogs sobre SQL Server según los devolvía google y acabé leyendo bastantes opiniones que se contradecían unas a otras relacionadas con las preguntas anteriores. En algún sitio ponían que los procedimientos almacenados eran compilados en el momento de ser creados… en fin…


Batch Compilation, Recompilation, and Plan Caching Issues in SQL Server 2005. Explica como funciona el caché de planes de ejecución con detalle para batchs (lotes) con sentencias SQL, incluidos procedimientos almacenados. También explica las diferencias principales de SQL Server 2000 y 2005 al respecto.


En el post 9.0 Memory Pressure Limits publicado en el blog SQL Programmability & API Development Team Blog, que siempre recomiendo para entender bien cómo funciona SQL Server por debajo encontré información adicional sobre el número de cachés de planes de ejecución y cómo se comportan en condiciones de poca memoria disponible. En el post What are the different cached objects in the plan cache? Ketan Duvedi nos explica los diversos tipos de objetos que se almacenan en las cachés de planes de ejecución


Por último en SQL Server Best Practices encontré una colección de enlaces a whitepapers y recursos con buenas prácticas para SQL Server (especialmente 2005)


Antes de responder a las preguntas conviene entender lo que es un batch o un lote de sentencias en SQL Server: Entenderemos por Batch o lote por un conjunto de sentencias SELECT, INSERT, DELETE, UPDATE, llamadas a prodecimientos almacenados. También se incluye todas las sentencias intermedias de Transact-sql, sentencias GRANT, DENY, etc… es la unidad de compilación e inserción en el caché pero cada sentencia individual. El batch es la unidad de compilación de plan de ejecución y cómo tal se guarda en la caché de planes de ejecución. Para cada sentencia del batch se guarda también su plan de ejecución en la caché, pero jerárquicamente dependen del batch.


Los Procedimientos almacenados se compilan? ¿cuándo lo hacen?


Pues primera discrepancia con lo que dije: los procedimientos almacenados estríctamente hablando no se compilan. Lo que se compila es el plan de ejecución y se almacena en la caché de planes de ejecución. El plan de ejecución se crea la primera vez que se invoca el procedimiento almacenado.  Y es tratado como una entrada más en la caché de los planes de ejecución, por tanto puede llegar a ser decartado. Por cierto los batch no debería superarse los 8KB de tamaño (ojo con los literales grandes, BLOBs, etc.) si queremos que entre la caché de planes de ejecución. Una mejora importante de SQL Server 2005 respecto a 2000 es que cuando es necesario recompilar el plan de ejecución de una sentencia en un batch (por diversos motivos, porque ha cambiado el esquema, etc.) se recompila solo esa sentencia y no el batch completo, como ocurría en SQL Server 2000.


¿Hay alguna diferencia en la forma de tratar la caché de los planes de ejecución marcados como prepared_query o stored proc?

A parte de ser objetos distintos (consulta parametrizada vs. proc. almacenado), en principio parece que no, según la documentación consultada ambos favorecen la reutilización de su plan de ejecución. De hecho las consultas marcadas como prepared query se han lanzado con SQLPrepare(), al igual que sp_executesql favorecen la reutilización del Plan. Los proveedores de OleDB y Odbc lo utilizan y como comprobé el proveedor nativo de SqlServer de ADO.Net con este hecho no hay diferencia entre tener una consulta prepared (parametrizada) y un procedimiento almacenado que realiza dicha consulta pasando los parámetros. Aunque parezca poco importante es muy revelador porque aun hay mucha gente que introduce consultas en procedimientos almacenados porque cree que es la única manera de aumentar el rendimiento al asegurar la reutilización del plan de ejecución.
Y puede que las consultas preparadas tengan mejor rendimiento que las incluidas en un procedimiento almacenado por la siguiente razón: Si un procedimiento almacenado se ejecuta en una base de datos D1, su plan de ejecución no es reutilizado si se ejecuta en una base de datos distinta D2. En cambio las consultas ad-hoc, las prepared y las dinámicas SI.

Los planes de ejecución se reutilizan entre usuarios, los contextos de ejecución (p.e. unos valores de parámetros determinados) no porque son afines a la sesión del usuario (lógicamente). Así que, aunque se cachean, el impacto es más bien escaso. Por eso es muy importante envíar consultas con parámetros explícitos o crear procedimientos almacenados, si bien SQL Server 2005 tiene una característica de autoparametrización de consultas que se repiten.

Es MUY IMPORTANTE utilizar nombres calificados a la hora de referenciar objetos de la base de datos. En otro caso no podrán SER REUTILIZADOS entre usuarios.

Por ejemplo: Supongamos que hay dos tablas foo: marta.foo y jaime.foo en una misma base de datos. El plan de ejecución «select * from users» para el usuario marta no sería compartido con el de jaime. Son tablas distintas. También ocurre cuando solo tenemos una tabla, por eso es importante hacer siempre «select * from dbo.users». En ese caso el UID coincide (-2) y puede ser compartida entre distintos usuarios.

 

¿Cual es el tamaño del caché de planes de ejecución?¿cúal es la política para descartar elementos?

Para empezar no hay un solo caché de planes de ejecución… realmente hay cuatro, pero dos son los importantes: hay uno para las consultas marcadas como ad-hoc y para las marcadas como prepared query y otro para los procedimientos almacenados. Podríamos pensar que estaría bueno balancear y crear prepared queries y proc. alamacenados para llenar equitativamente las dos cachés. Es una tontería, porque en el caso de que uno se llene y queden libres en el otro se le asigna el espacio sobrante al que le hace falta.

 

Con la siguiente consulta obtenemos los siguientes tamaños de entradas para cada plan de caché:
 

select name, type, buckets_count from sys.dm_os_memory_cache_hash_tables
where type in (‘CACHESTORE_OBJCP’ , ‘CACHESTORE_SQLCP’, ‘CACHESTORE_PHDR’, ‘CACHESTORE_XPROC’)

 

Por ejemplo en mi sistema (SQl Server Express 2005)

Object Plans               (CACHESTORE_OBJCP) 10007
SQL Plans                  (CACHESTORE_SQLCP) 10007
Extended Stored Procedures (CACHESTORE_XPROC) 127


Respecto a la política para descartar elementos se basa en el espacio de memoria reservado y la reutilización del plan de ejecución. En ningún momento se desprende un trato especial a los procedimientos almacenados. Concretamente en SQL Server 2005 hay ciertas diferencias con la política de caché de SQL Server 2000. Los cachés de planes de ejecución y de datos son distintos.  El lazy-writter que decrementa periodicamente el coste de un plan de ejecución (y si es 0 en caso de poca memoria se descarta) en SQL Server 2000 no existe como tal en SQL Server 2005. En vez de eso, cuando las entradas en el caché superan el 50% del tamaño destinado, el siguiente plan que llega al caché decrementa el contador de coste en 1 de todos los anteriores. Realmente este decremento es marcado a lo cerdito (piggybacked) y no es inmediato por el comportamiento, en la práctica, es parecido al del lazy-writter de SQL Server 2000. Cuando el tamaño del caché supera el 75% un hilo con un monitor dedicado se activa y decrementa el coste de los objetos en todas (ojo todas) las cachés. Si el plan se vuelve a utilizar se resetea este contador.


¿Cuándo se invalida el plan de caché, datos etc y se precisa de recompilación?


En bastantes circustancias, solo comento las principales (mirar mejor la documentación mencionada) y se agrupan en dos:


A. Razones relativas a la corrección del plan



A.1. Modificación en el esquema que afecta a la sentencia (p.e. se borra una tabla)

A.2. Llamadas a sp_recompile en un proc. almacenado, logicamente 😉 o que fuese creado con la opción WITH RECOMPILE


A.3. Operaciones que causan la invalidación total del plan de caché, a saber: Hacer un detach. Actualizar una bd (de 2000 a 2005). Ejecutar DBCC FREEPROCCAHE. Comando Reconfigure. Comando ALTER DATABASE … MODIFY FILE GROUP o COLLATE. Reiniciar el servidor o el servicio de SQLServer también causa la invalidación total de los planes  (lo he comprobado)


A.4 Cambios en alguna opción SET antes de la consulta: ANSI_NULL_DFLT_OFF, ANSI_NULL_DFLT_ON, ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, DATEFIRST, DATEFORMAT, FORCEPLAN, LANGUAGE, NO_BROWSETABLE, NUMERIC_ROUNDABORT, QUOTED_IDENTIFIER. Es mejor establecerlos al principio de la conexión o de la sesión o dejárlos fijos en la BD.



B. Razones relativas a nivel de optimismo del plan.


B.1 Cuando todas las tablas de la consulta están marcadas como de solo lectura el plan no se recompila. Si la consulta tiene la opción KEEPFIXED tampoco se recompila 

B.2. Cuando ciertos indicadores dicen que la consulta ya no es óptima y necesita ser recompilada… depende de bastantes factores como por ejemplo las estádisticas referentes a las tablas implicadas y su tipo (si es variable, temporal, etc.). Para más info verlo en el documento en la sección «Plan optimality.related recompilations: The Big Picture».

Evidentemente, aunque no lo incluye el documento queda la opción de que el plan de ejecución sea descartado de la caché por problemas de espacio.


Conclusión

Los planes de ejecución de los Procedimientos Almacenados son creados en la primera invocación de la instancia del servidor SQL Server y son tratados como una entrada más en la caché de planes de ejecución. No parece que haya ninguna ventaja en utilizar procedimientos almacenados en SQL Server 2005 para mejorar el rendimiento de consultas preparadas, especialmente en lo que se refiere al caché de planes de ejecución. Por lo que no compensa su creación.