Jugando con System.Transactions

Sin duda la capacidad de usar ámbitos de transacción es un gran avance en lo que a legibilidad, mantenibilidad y claridad de interfaces se refiere. Se termina la necesidad de ir pasando objetos que representen una transacción de un método a otro para que las operaciones de datos puedan realizar en la misma transacción.

Si bien no es una novedad radical, puesto que el mismo efecto se podía lograr usando el namespace System.EnterpriseServices y derivando nuestra clase de ServicedComponent, el hecho de que ya no sea necesario derivar de ServicedComponent hacer que nuestras jerarquías de clases sean mucho más naturales.

Además las transacciones de System.EnterpriseServices siempre están controladas por el DTC (Distributed Transactions Coordinator), lo que añade más coste de ejecución a estas. Usando System.Transactions, sin embargo, las transacciones solo se promocionan a transacciones del DTC (transacciones distribuidas) cuando es necesario, y en principio, eso solo es necesario si estamos tratando con diferentes fuentes de datos.

Hasta aquí las buenas noticias. La mala noticia es que si utilizamos diferentes conexiones para realizar operaciones de datos, aunque sean a la misma fuente de datos, y con la misma cadena de conexión, las transacción se promociona al DTC. Esto nos deja en la misma situación que queríamos evitar, puesto que si queremos evitar esto debemos pasar la conexión como parámetro de nuestros métodos.

Cuando David Carmona y Unai Zorrilla, en su visita a Bilbao para la gira de presentación de VS 2005, en la que los tres participábamos, me comentaban esta situación no me lo podía creer. Luego pensé, vale, se escala al DTC y eso no es bueno, sin duda, pero como de malo es, ¿cual es el coste de escalar al DTC y como afecta al rendimiento de la aplicación?

Y dispuesto a contestar esta pregunta, he abierto el VS esta mañana. Y puedo adelantar que no me gusta lo que he descubierto. Sin bien también he de reconocer que el entorno utilizado para las pruebas no es la mejor, un portátil mono procesador, corriendo Sql Server, el DTS y las pruebas. En cuanto pueda las repetiré en un servidor.

Me he hecho un programita en C# que podéis descargar DTCTest Source Code, que básicamente lo que hacer es una serie de inserciones en una tabla en una transacción, una vez usando la misma conexión, otras usando dos conexiones diferentes, lo que hacer que la transacción se promocione al DTC. El programa hace esto de 1000 a 10000 veces en intervalos de 1000 y muestra el tiempo que tardó la operación. 

El primero problema que he encontrado ha sido ¿Cómo saber cuando las transacciones están promocionándose al DTC? He encontrado dos soluciones, mediante programación, utilizando el evento DistributedTransactionStarted de la clase TransactionManager. Evidentemente esto no me sirve del todo para mis pruebas, puesto que controlar este evento, como es evidente y la propia documentación advierte penaliza el rendimiento de las transacciones.

El segundo método es simple, menú Inicio->Herramientas administrativas->Servicios de Componentes. En el árbol que aparece, Sevicios de Componentes->Mi PC->Coordinador de Transacciones->Estadísticas de Transacciones. Esta pantalla muestra la cantidad de transacción distribuidas en progreso.

El segundo problema es algo curioso:

¿Qué pensaría cualquier programador de un código como este?


cn2.Open();
cn3.Open();

using (TransactionScope t = new TransactionScope(TransactionScopeOption.RequiresNew, tso))
{
   
cm.Connection = cn2;
   
cm.ExecuteNonQuery();
   
cn2.Close();
   
   
cm.Connection = cn3;
   
cm.ExecuteNonQuery();
   
cn3.Close();
}
 

Lógicamente como no se llama a t.Completed() antes de salir del ámbito de la transacción la misma no debería completarse, pero lo que en realidad ocurre es que los cambios si se realizan en la base de datos. Este código en apariencia hace un cosa y en la realidad otra. El motivo es que lo que marca que la operación de base de datos este dentro o no del ámbito de transacción es cuando se llama al método Open de la conexión que se utiliza para realizar la operación de base de datos. Esto es propenso a errores, porque lo que todo programador tiende a pensar es que es suficiente que la ejecución del comando esté dentro del ámbito de la transacción.

Con los resultados de mi prueba he construido el siguiente gráfico:

Este gráfico deja claro que, sin el DTC de por medio, el tiempo de ejecutar las transacciones crece linealmente. Lo problemático es que cuando el DTC entra en juego. Este tiempo se va incrementando exponencialmente y esta es una pésima noticia, porque significa que a más transacciones, más coste por transacción, lo cual es extremadamente negativo para la escalabilidad de nuestras aplicaciones. No me puedo creer estos resultados, por lo tanto estoy revisando el procedimiento de prueba que he realizado y no considero nada definitivo hasta que no corra estas pruebas en un entorno más adecuado. Ya os contaré…

ACTUALIZACIÓN: Debes leer Sobre las transacciones, las conexiones, el LTM y el DTC de Iván González!!! Desentraña el misterio de la escalabilidad exponecial.

2 comentarios sobre “Jugando con System.Transactions”

Deja un comentario

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