Transacciones Distribuidas en .NET 2.0

 Quienes  han trabajado en proyectos empresariales grandes, les habrá tocado integrarse a diversos subsistemas de la organización (o eventualmente, de terceras partes)

Un problema que existió desde tiempo inmemorial fue el hecho de mantener la consistencia global del estado, entre todos los subsistemas, en la medida en que las aplicaciones eran utilizadas. Esto es, que no ocurra que una modifique datos sin respaldar las consecuentes equivalencias en los demás subsistemas

Ejemplo, si el subsistema de recaudación informa que el cliente adeuda tres pagos, por lo que es necesario bloquearle el servicio, el subsistema de operaciones debe efectivamente bloquearle los servicios en tanto que el subsistema de legales debe generar una entrada para comenzar el proceso judicial con este cliente. Asimismo, el subsistema de CRM (Customer Relationship Management, o Administración de la Relación con Clientes) debe reflejar que el cliente está en este estado judicial y sus cuentas bloqueadas. Todo esto no puede ocurrir parcialmente, o bien ocurre completamente, o no debería ocurrir en absoluto. Pero si ocurre a medias, podría pasar que mientras el cliente tenga bloqueados los servicios, sin que exista una causa judicial en su contra

Por eso, se dice que el movimiento de mandar un cliente a Legales es un movimiento transaccional. O pasó todo, o no pasó ninguno. Y en estos casos se dice que una transacción tiene propiedades ácidas   :-D   Veamos:

 
  • Atomic (Atómica): es que no acepta subdivisiones. Todo o nada, como decía antes
  • Consistent (Consistente): en el durante de una transacción se permite cierto grado de consistencia en la medida en que se va ejecutando, pero una vez que se completa, el estado final debe ser consistente. O bien, si se anula (y por la propiedad anterior se debe anular todo), se retorna al estado inicial que también debe ser consistente
  • Isolated (Aislada): no depende de otras transacciones que estén ocurriendo en ese momento
  • Durable: implica que una vez realizada la transacción, la vuelta atrás sólo es posible por una transacción de reversa. Esto es, si hice un crédito y me equivoqué, tengo que hacer un débito para anularlo. No puedo esperar que se anule sólo

En aquellos sistemas que no tienen soporte transaccional desde la infraestructura, la acidez hay que soportarla a manopla: el código de la aplicación se tiene que encargar de revertir movimientos que quedaron a medias… si es que llega a detectar que hubo movimientos que quedaron en estado inconsistente. Logicamente, es un círculo vicioso ya que la reversa, al no haber soporte transaccional, a su vez puede quedar hecha a medias, y así sucesivamente

Entonces volvamos al mundo feliz de los sistemas montados en infraestructuras que sí manejan integridad transaccional. Acá es la misma infraestrctura la que nos habilita, en rasgos generales, las siguientes primitivas:

  • Inicio de Contexto Transaccional
  • Cierre Exitoso de Contexto (familiarmente, "Commit" por ser esta la sentencia con que se les pide a las partes involucradas en la transacción que confirmen el cambio de estado)
  • Cierre Fallido de Transacción (comunmente "Rollback" o vuelta atrás)

En aplicaciones distribuidas, multi back-end, estas primitivas se le envían a cada parte interviniente en la transacción. Two-Phase Commit (Concreción en Dos Fases o 2PC). Tener que hacer la reversa a mano en sistemas distribuidos es poco menos que dantesco. En integración de aplicaciones dentro de la organización se aplica 2PC. Pero en integración B2B por un largo tiempo habrá que seguir compensando a mano

Lo cierto es que MS .NET 2.0 incorpora una API llamada System.Transactions. La misma tiene una clase fundamental llamada TransactionScope. Representa un contexto transaccional. Usarla es lo más simple del mundo. Mirá el siguiente código

using (TransactionScope scope = new TransactionScope())
{
   // lógica transaccional
   …

   scope.Complete();
}

En la sentencia using se está creando el contexto transaccional. Y al cerrarse el corchete de ese bloque de código, si al contexto creado se le invocó el método Complete(), un comando Commit es emitido. En cambio, de no haber sido invocado ese método, será un Rollback

Adentro del bloque, los diferentes recursos distribuidos se van enlistando en el contexto. APIs como ADO.NET se enlistan solos, lo mismo pasa con EnterpriseServices (COM+). Otros componentes se deben enlistar explícitamente en el contexto

Lo fantastico es que esta API de transacciones habla por debajo nivel -siempre que más de un back end esté enlistado- con MS DTC (Distributed Transaction Coordinator), un componente del sistema operativo que se encarga del transporte a bajo nivel de mensajes de coordinación transaccionales

Una joyita realmente. Si venías haciendo reversas a manopla, o preguntando por flagcitos para saber si cancelar o no un movimiento, lo que necesitás realmente es conocer esta API

Más info al respecto se encuentra acá

Si querés ver esto en código que ejecuta, en el próximo número de MSDN Magazine van a hablar al respecto. Te lo anticipo en: http://msdn.microsoft.com/architecture/default.aspx?pull=/msdnmag/issues/06/09/NETMatters/default.aspx
Una forma rápida de verla funcionando, no te toma más de 15 minutos, es un demo que grabaron en MSDN TV (a ver, fue lo que miré antes de escribir el post ;-): Introducing System.Transactions in .NET Framework 2.0

Y para cuando quieras subirte en serio, escribiendo tus propios recursos transaccionales -esto lo estoy agregando gracias a un feedback de Germán Matosas, consultor de MS Cono Sur y el lector más madrugador de este blog :-D- hay un par de links ad hoc

Arrivederci

Esta entrada fue publicada en Software Architecture. Guarda el enlace permanente.

10 respuestas a Transacciones Distribuidas en .NET 2.0

  1. German dijo:

    El factor diferencial mas importante de la System.Transaction es que inicia una transax simple y hace el switch a una distribuida (DTC) cuando un segundo RM es involucrado. Esto exige un soporte del RM involucrado, y hasta el momento solamente está presente en SQL 2005. Desde el punto de vista de la performance las ganancias con esto son muy importantes (el DTC agrega overhead p/tranx).
     
    -germán

  2. Diego dijo:

    Muchísimas gracias por la aclaración, Germán!!!Efectivamente, acá estaba leyendo sobre lo que comentás y me faltó poner un par de links fundamentales para todo aquel que quiera que su servicio, ya existente, pueda ser coordinado dentro de un contexto distribuido:Writing a Transactional Application
    Implementing a Resource Manager
     
    (prometo actualizar el post para que quede todo en un solo lugar  😉
     
    German, llegaste a probar la System.Transactions? Alguna experiencia para compartir? Te mando un abrazo y gracias por el aporte

  3. German dijo:

    Diego, he usado la System.Transaction y agrego un poco mas de background resumido: en 2.0 se incluyeron 2 transaction managers, el LTM (lightweight) y el OLETx (similar al DTC). El LTM es usado por defecto y solamente intra-app domains. Si hubiera un salto entre app-domains, entre procesos o si un RM nuevo es involucrado en la transaccion entonces es "promovida" a OLETx (dado que el objeto Transaction debe ser serializado para ser pasado por RPC entre procesos y esto lo maneja OLETx).
     
    Para esto el RM debe ser capaz de negociar la promoción y esto lo hace si implementa una determinada interfase (IPromotableSinglePhaseNotification, que contiene -entre otros- un metodo Promote). Actualmente solamente SQL 2005 implementa esta interfase, pero es de esperar que otros se vayan incorporando.  Si la transacc se inicia contra un RM que no implementa la interfase anterior entonces se usa OLETx directamente.
     
    Si trabajamos entonces contra SQL 2005 de la forma que comentas en el post es de esperar una performance de la transaccion igual a una transaccion nativa (de ahi lo de "Lightweight"), con la gran ventaja que el eventual switch a OLETx se realizará de manera transparente para el programador, lo cual hasta ahora no era posible dado que los modelos de programación eran diferentes en ambos casos.
     
    Saludos,
    -germán

  4. Diego dijo:

    Realmente apreciables tus comentarios, Germán. Desde ya mil gracias, no aparecen en el resto de la documentación y sirven para ganar "insight" rápido con esta APIPareciera ser que lo destacable más allá del nivel de implementación aún disponible -como comenta Germán, hasta ahora sólo SQL Server 2005- es el nivel de transparencia respecto del código desarrollado con esta APIInvito a cualquiera que haya tenido experiencias con la misma, a que vuelque sus comentarios, conclusiones, "good + bad + ugly" (lo bueno, lo malo y lo espantoso) de la mismaQué me motiva a darle tanta manija a esta API: experiencia personal. En la 2da mitad de los 90 trabajaba para una compañía de telefonía celular. Tenían un sistema que era una pieza de colección del punto de vista teórico, pero algo no tan rutilante en cuanto a implementaciónEra extraordinaria la capacidad de interoperabilidad de este sistema con diversos equipos de telefonía (switchers para telefonía celular, pagers, trunking, plataformas de telefonía a crédito, servidores de Internet -correo electrónico, navegación web, etc-)Todo desde el subsistema de CRM. ImpecablePero…Toda la interoperabilidad no era transaction-oriented. Esto en la práctica implicaba que si quería dar de baja a un tipo por moroso, había que anularlo en todos los back-ends de los servicios a los que estaba apuntadoSi fallaba, y era bastante frecuente que ocurra, alguna de las bajas en los back-ends, había que terminar la oeración manualmente. Es decir, dos caminos: o se hacía un rollback manual para volverlo a poner en la posición previa y dejar que el mismo sistema lo cancele (generando los movimientos contables y otros respaldos necesarios), o se completaban los pasos que fallaron a manopla… Ambas salidas muy dolorosas. CostosísimasClaro, se podía pensar en un soporte transaccional pero el problema era doble:En aquel entonces las APIs eran muy complejas, si existían, y la programación de un two phase commit era bastante opaca (es decir, lo opuesto a transparente)El otro problema era que muchos de los back-ends no estaban preparados para participar en transacciones. Vale decir, imaginate que una de los back-ends fuera una basesita en Clipper. Si hacías un APPEND -mirá cómo me acuerdo de Clipper- o un REPLACE, lola. Si entró no había forma de darle rollback. Esto es: no estaba preparado para participar en transaccionesEl tema me parece no menor. Me gusta esta API por la transparencia que expone. Creo que la JTA (Java Transaction API) de Java EE no ofrecía la misma simplicidad, al menos en su especificación. Javeros, a tomar el guante y responder si no están de acuerdo con esto último
     
     
    Abrazo a la perrada

  5. Unknown dijo:

    Me parece muy bien que MS tenga en esta nueva version de .NET y en conjunto con SQL 2005 un mejor tratamiento a las transacciones de doble fase (de nivel empresarial generalmente). La version anterior dejaba mucho que desear en relacion al enmascaramiento de las componentes .NET como si fueran COM+ para subirlas recien al Component Service. Que por cierto pegaba mucho en el performance y no resultaba comodo para los programadores. Aunque no conosco el modelo total en esta nueva version en relacion a componentes transaccionales, al parecer los problemas anteriores han sido superados, y me parece muy interesante que en este nueva version exista al menos con SQL 2005 una forma de economizar recursos transaccionales cuando de transaccion local se pasa a distribuida. Creo no haber sabido de ello antes. Muy Bueno.Saludos CordialesJavier Urrutia Tobarhttp://MisBytes.wordpress.com

  6. German dijo:

    Agree con Javier en esa especie de oscuridad del mundo de los "Enterprise Services" (COM+), no tanto en su definición sino en las particularidades de su deployment.
     
    Relacionado con este punto, por si están actualmente trabajando con esto y para redondear el tema, System.Transaction soporta nativamente los Enterprise Services de .NET. Los Serviced Components "actuales" (COM+), sin modificación de código, deben ejecutar correctamente sobre 2.0. Es mas, desde dentro de un TransactionScope se puede llamar a un Serviced Component, y viceversa. El nivel de interoperabilidad se fija en el constructor del TransactionScope (opción EnterpriseServicesInteropOption) y ese mix produce efectos interesantes (pero ese es tema para otro post de Diego😉.

  7. Diego dijo:

    Gracias una vez más, estimados, por los comments. Germán, as usual. Javier, por tu blog (y la "primera mascota perruna", como la llamaste)
     
    Les comento que generé una versión en inglés para SkyScrapr.net, el sitio de los aspirantes a arquitectos. La entrada está en http://www.skyscrapr.net/blogs/solution/archive/2006/08/14/273.aspx

  8. Richard dijo:

    Buenos dias..
    Estoy tratando de crear un aplicacion distribuida y esta me esta generando un error al tratar de insertar registros en bases diferentes como prodria solucionar este inconveniente.
     
    Estoy insertando un registro en una base y la auditoria de ese registro en otra base, al tratar de insertar el ultimo este me genera el siguiente error:
    Error de comunicación con el administrador de transacciones subyacentes.
     
     

  9. Unknown dijo:

    gold wow
    gold wow
    gold wow
    gold wow
    gold wow
    gold wow
    gold wow
    gold wow -233852294054164

  10. Claudio Aguilar dijo:

    Excelente Dieguito!!! justo lo que andaba buscando

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s