EF 6: Buffering versus Streaming

Hace poco se incorporó un change set con un nuevo ‘breaking change’ dentro de la rama principal de Entity Framework. Este ‘breaking change’ se produce por el cambio en el modo de ejecución de las lecturas por parte de nuestras unidades de trabajo, pasando del modo habitual de lectura en streaming con un DbDataReader a un modo buffering, es decir, a la lectura temprana de los datos y al almacenamiento de los resultados en un buffer. A lo largo de esta entrada intentaremos ver  desde que supone para nosotros este cambio como desarrolladores, hasta las justificaciones para realizarlo, pasando como no, por algunas piezas de código que nos permitan entender mejor como lo han implementado.

El cambio

Cuando trabajamos con EF generalmente, solemos tener cosas como la siguiente:

 

Y todos tenemos claro que ese enumerador es una consulta diferida, técnicamente por un shaper, la diferencia de este trabajo en EF 6 con respecto a las anteriores estriba en como se hace la materialización de este enumerable. En la versión actual de EF tendremos un data reader que será usado en cada llamada al MoveNext del iterador. Aunque todos sabemos que internamente DataReader funciona con bloques para conjuntos de datos grandes estos se van incorporando a medida que se necesitan, es decir, hay un proceso de streaming de los datos del servidor a cliente. Para ver esto solamente tendríamos que realizar una lectura de un cojunto grande y ver como el BatchStarting y el BatchCompleted sucenden al comienzo de la lectura y cuando hemos iterado por todos los resultado, es decir, para la siguiente consulta, podríamos ver los siguientes valores en nuestro profiler

 

Comienzo de la lectura

Untitled1

 

final del bucle

Untitled2

 

Lógico según lo se sabemos sobre el comportamiento de nuestros data readers ¿ verdad ?. Bien, pues ahora intentamos hacer esto mismo con EF 6 y veremos lo siguiente nada más ejecutar el paso por el primero de los elementos…

 

image

 

La razón es la que comentamos más arriba, en la nueva versión de EF que tendremos el mecanismo de consultas reemplazará el DbDataReader por un BufferedDataReader (System.Data.Entity.Core.Objects.Internal.BufferedDataReader) el cual en realidad no es más que un envoltorio de un DataReader que hace todo la lectura de sus elementos en su inicialización y los almacena en una estructura interna BufferedDataRecord, de ahí el nombre de Buffered, que será usada por el materializador ( shaper ) para crear las distintas entidades.

Aunque esto pueda parecer un mero cambio interno a EF, en realidad no es tan sencillo y supone un breaking change porque tiene un impacto sobre las aplicaciones actuales. De entrada es lógico ver que hay una mayor presión de memoria, aunque sea memoria de generación 0, puesto que estamos duplicando los datos en el proceso de materialización. El segundo motivo es que por ejemplo si se rompe el bucle anterior por cualquier condición en la versión anterior no tendríamos porque haber traído todos los bloques del reader mientras que en EF 6 si habríamos traído todos los bloques del reader. Otro impacto posible lo podemos observar en la llamada a nuestro GetEnumerator, puesto que ahora necesitará más tiempo para completar el trabajo.

 

Los motivos

 

Las razones para movernos de un modo Streamed a Buffered se deben principalmente y tal como el equipo ha explicado en el siguiente design meeting a las siguientes razones:

 

  • Sql Azure connection resilience: Como todos sabéis, el trabajo con Sql Azure tiene una problemática especial que nos obliga a necesitar en ciertos escenarios de reintentos en nuestro trabajo con EF, Cesar de la Torre escribió hace ya mucho tiempo sobre esto. Dentro de la iniciativa del equipo por hacer más robusto el uso de EF con Sql Azure este cambio es necesario ya que no retarda el uso de una conexión más allá de lo necesario.
  • El tiempo de uso de una conexión se reduce potencialmente
  • Ya no es necesario MARS  ( obligatorio con lazy loading en la versión actual )

Y si quiero mi antiguo comportamiento

Bien sea por compatibilidad o porque el escenario así lo requiera, en EF tendremos un mecanismo para seguir usando streaming en nuestras consultas, para ello, solamente tendremos que utilizar el método AsStreaming, gracias al cual EF volverá a usar un DbDataReader en lugar del ya mencionado BufferedDataReader. A continuación podemos ver un ejemplo de ello:

 

 

Bueno, hasta aquí hemos llegado, espero que todos tengamos en cuenta este cambio si decidimos migrar aplicaciones de EF 5 a EF 6 puesto que tal y como hemos dicho puede tener impacto y producirse situaciones no deseadas sino nos fijamos…

 

Saludos

Unai

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *