Speed of Light: ¿Usa el DataReader cursores?
Hace unos días tuve una agradable conversación con unos compañeros que nos acabó llevando al espinoso asunto del uso de los cursores... momento en el cual este tipo de conversaciones suelen dejar de ser tan agradables y pasan a la categoría de Guerras de Religión (ya sabéis, cursores siempre vs. cursores nunca, vi vs. emacs, Windows vs. GNU/Linux y como no, el rey de las guerras de religión... Aritos de Cebolla vs. Fingers de Mozarella).
El caso es que yo, habiendo entrado en modo talibán durante el ejercicio de mis labores como detractor del empleo de los cursores, estaba defendiendo que no era la mejor de las ideas utilizar cursores para un proceso que podría trabajar potencialmente con millones de filas. Me quedé sorprendido cuando otro chico me comentó que los DataReaders de .NET estaban implementados mediante cursores, y ciertamente recordaba haber leído algo similar durante mi formación en ADO.NET, por lo que volví a la documentación de la MSDN y pudimos comprobar cómo los DataReaders funcionan mediante cursores forward-only, read-only.
Antes de que hubiéramos terminado de leerlo ya teníamos a Unai picando un código de demo :_) Cuando la terminó, la ejecutamos y monitorizamos su ejecución mediante el SQL Profiler (capturando eventos de cursores de servidor y de cliente... aunque los de servidor no debería aparecer nunca con ADO.NET, pero por si las moscas ;)), el performance monitor de windows (perfmon.exe) y las vistas de administración dinámicas de SQL Server 2005, y no pudimos apreciar la creación de un solo cursor.
Por una parte fue un alivio (nos parecía overkill que se crearan cursores con todos los DataReaders), pero por otra parte resultó bastante desconcertante el hecho de que en la documentación se hiciera referencia al empleo de cursores, pero en lo práctica no hubiera rastro de ellos. Tras unos minutos de investigación y el comodín de la llamada (gracias Nacho!!) descubrimos que los cursores forward-only, read-only en ADO.NET son los firehose cursors de ADO, que para empezar no son cursores, y por otra parte tampoco apagan fuegos! xD
En realidad, estos 'cursores' son solo un mecanismo para mover de modo rápido datos de un servidor SQL Server al cliente que los ha solicitado. En la práctica, lo que hacen es enviar directamente los datos solicitados por cliente al output buffer del SQL Server. Cuando éste buffer se llena, espera hasta que el cliente pueda recoger estos datos y lo vuelve a llenar de nuevo. Este proceso se repite una y otra vez hasta que el cliente puede recuperar todos los datos.
Como se puede ver, no es el cliente el que va solicitando items como en un cursor tradicional, sino el servidor quien envía todo lo que puede la output buffer. Los bloqueos solo se mantienen mientras el servidor mueve los datos a éste buffer, de modo que no depende de la velocidad de recogida de los datos por parte del cliente, lo que ayuda a mejorar considerablemente el rendimiento y concurrencia.
En definitiva:
- Cuando veamos en la documentación de ADO.NET un cursor forward-only read-only, no nos llevemos las manos a la cabeza. No son cursores de verdad, y su rendimiento y bloqueos no tienen nada que ver con un cursor ad hoc.
- Lo importante no es saber, es tener el teléfono del que sabe xD
- Los aritos de cebolla molan 1024, pero los fingers de queso tiene un mayor Cool Factor(tm).