El caso es que la semana estuve midiendo tiempos y rendimiento de diferentes
sentencias DML lanzadas contra MySQL. Más que nada, me interesaba analizar el comportamiento de los
procedimientos almacenados o stored procedures que MySQL soporta a partir de la versión 5.0, frente a las clásicas sentencias en texto plano de
select, insert, update y delete. Aunque existen herramientas tipo
query analyzer que exprimen todo el jugo a cada consulta y miden tiempos y rendimiento de manera bastante precisa, me apetecía medir tiempos desde el mismísimo campo de batalla, desde la trinchera del programador, a pie de código. Inmejorable ocasión por tanto para probar
MySQL Connector/NET 5.2 (última versión de este conector disponible a día de hoy) desde una aplicación de consola
dummy programada en
C#. Aparte de que quería probar este conector, tiene su lógica hacerlo de esta manera, ya que hoy en día toda aplicación que necesita acceder a base de datos utiliza c
onectores de diversos tipos dependiendo de la tecnología y lenguaje de programación que se esté utilizando.
Sin más preámbulos ni rollos
macabeos, me centraré (por fin) en narrar lo que me pasó durante estas pruebas realizadas contra una
base de datos MySQL remota, aunque dentro de las misma red que mi equipo de trabajo. Como siempre, todas estas pruebas y comparaciones suelen empezar suavecito, con sentencias
select no demasiado complicadas, 1000 inserciones en una tabla, etc. Hasta aquí todo bien, tiempos parecidos, incluso ligeramente mejores para las
queries en texto plano. Palabras mayores, paso a ejecutar un programita que realiza 100000
(cien mil) inserts en texto plano dentro de un bucle
for en C#, es decir, ni más ni menos que 100000 llamadas a
ExecuteNonQuery(). La ejecución tarda un rato, casi dos minutos, pero va bien, las filas son insertadas como Dios manda (¿Cómo manda Dios?).
Pasamos entonces a ver qué tal funciona lo mismo, pero haciendo una única llamada desde código al método
ExecuteNonQuery(). ¿Cómo? Llamando (a gritos) a un
stored procedure que realiza las mismas inserciones, aunque en un
loop interno definido en el propio procedimiento almacenado, no a nivel de código cliente. ¡Vamos allá!

¡Pues va a ser que ha petado! A ver qué dice el error lanzado por el conector..."
Fatal error encountered during command execution".
Buff, ¡qué mal rollo
Arroyo! Sinceramente, me he quedado igual o peor, no tengo ni idea de qué está pasando, y el error lanzado no es de gran ayuda, como ocurre desgraciadamente muchas veces.
Vuelvo a ejecutar varias veces lo mismo con idéntico resultado, hasta que me apiado y paulatinamente voy bajando el número de inserciones del procedimiento almacenado hasta llegar a un punto en que ¡funciona! ¿Tendrá MySQL un límite de memoria/hilos/procesos en los procedimientos almacenados? No creo..., entonces, ¿será cosa del conector? Mientras me hago esta última pregunta me quedo mirando fijamente la consola, concretamente mi atención se centra en el tiempo cronometrado (por mí) que se muestra tras el error, 30 segundos y pico. ¡Ajá! Ya caigo..., me suena que las sentencias
SQL tienen un
timeout, es más, me suena que ese
timeout suele tener por defecto un valor de 30 segundos. Vamos a tirar de la ayuda, que para algo la suelen realizar...

Efectivamente, confirmado queda que el comando para
atacar la base de datos tiene un
timeout por defecto de 30 segundos, lo que implica que si el comando no termina, se dispara un error. Claro, ahora todo coincide ya que seguramente la ejecución del
stored procedure dura más de 30 segundos, lanzándose la excepción nada aclarativa recibida. ¿Solución? Establecer un valor más alto para dicho
timeout. Aunque no se recomienda tocar este parámetro ya que es difícil que la ejecución de una
query o
stored procedure dure más de 30 segundos (excepto en las monstruosas transacciones y procesos
batch que realizan bancos y demás), esta vez vamos a añadir una simple línea de código C# asignando un valor entero muy alto al
timeout del
command, pa' cubrirnos las espaldas ;-).

Compilamos, y ¡allá vamos de nuevo!

¡
Voilá! Como se puede apreciar en la imagen de arriba, las 100000 inserciones se han realizado perfectamente, en un tiempo ligeramente inferior a las 100000 inserciones realizadas "a pelo".
Como adelantaba al principio del
post, aunque en este caso particular el ejemplo se ha desarrollado con el conector de .NET para MySQL, esto mismo sirve para otros conectores, tanto a MySQL, como a Oracle, SQL Server, Sybase, o el repositorio de datos que sea. De hecho,
si no me equivoco, todos los conectores a base de datos vienen por defecto con un timeout por comando de 30 segundos, tiempo más que suficiente en el 99.99% de los casos, y si no anda Lonifasiko por ahí cerca con ganas de marcheta ;-).
Tened en cuenta por tanto que el
timeout en los accessos a base de datos existe, y aunque rara vez se da, hay casos en los que se supera y es necesario reajustar su valor manualmente por código. Soy el primer
timeout-breaker, o ¿tiene alguien alguna otra experiencia similar que contar con otro conector hacia MySQL o hacia otra base de datos? Seguro que sí...
SaludoX.