martes, 7 de octubre de 2008

Cuidado con XA y JMS (o "Estoy hasta los huevos desde hace años")

Pues lo de casi siempre:

...mucha capa de abstracción, mucha transparecia para el desarrollador y mucha magia... y a la hora de la verdad hay que remangarse y entenderlo todo.

Porque en Java CAPS las colaboraciones que interactúan dentro de un conectivity map en el fondo son MDBs. Y los MDBs en el fondo son EJB disparados por colas JMS. Y por defecto las transacciones JMS son XA, con dos cojones.

Es decir: si tengo unas cuantas colaboraciones (en Java CAPS, léase "unos cuantos EJBs" para una visión más generalista del mismo problema) que interactúan "desacopladamente" entre si (¡qué gran palabra! casi a la altura de "sinergia" y de "talento"...), o un master que va lanzando colaboraciones/MDBs de forma asíncrona o síncrona (request/reply), parece lógico pensar que todos los recursos a los que se accedan estén en modo XA... al menos, si no pensamos, los declaramos así :-)

Nota: el objetivo de este artículo no es explicar el patrón request/reply, pero ahí dejo un diagrama ilustrativo (enlaza con la explicación del patrón en docs.sun.com):
Ya primera gran mentira viene ahí, porque en el 99% de los casos no sólo accedemos a bases de datos sino que también escribimos en logs o en ficheros a disco, o interactuamos con un monitor transaccional o hacemos una petición http o TCP cruda... Llevo más de 6 años sin ver dónde está la magia del XA y viendo sólo dolores de cabeza, pero todo esto es otra cuestión...

...Lo que viene al caso para este artículo es que si en una colaboración Java CAPS (o en un MDB normal si funcionamos fuera de un integrador) invocamos a base de datos con transacciones XA y la invocación JMS a ese componente estaba también en modo XA, se produce un deadlock: como la inserción/modificación falla, el sistema decide deshacer también el envío a la cola JMS y por supuesto no llega nunca a enviar el mensaje de respuesta a la colaboración maestra indicándole el fallo (para que desde ahí se reaccione o lo que sea); en lugar de eso, la invocación al segundo componente vuelve a dispararse porque la cola estaba en modo XA. Y vuelve a fallar la inserción. Y así indefinidamente.

Esto que he explicado fatal está perfectamente explicado en el siguiente artículo: http://blogs.sun.com/fkieviet/entry/request_reply_from_an_ejb.

La solución: en modelos síncronos (request/reply) nunca usar modo XA para el envío del mensaje de request, sino ponerlo a modo TRANSACTED. Y ya que estamos, si sólo interactuamos con una instancia de base de datos, el recurso JNDI tampoco lo definamos en modo XA sino normal. Manténgase en modo XA sólo las invocaciones asíncronas de verdad y si queremos evitar posibles duplicados en inserciones. Pero mi apuesta es por no usar XA para nada y gestioanr las cosas a mano: porque teniendo el control, tendremos toda la capacidad de reacción con incidencias en el entorno productivo en lugar de liarnos a intentar modelizar mentalmente qué narices está pasando.

Porque además, en nuestro caso, hemos ganado un 50% de rendimiento en algunos pasos y el servidor de aplicaciones va mucho más ligero y con menos sobrecarga de recursos y cosas raras... (¿no iba este blog sobre rendimiento?).

Como digo, véase el artículo referenciado (no habla de Java CAPS en particular sino de EJB y JMS en general): http://blogs.sun.com/fkieviet/entry/request_reply_from_an_ejb.

Ciao ciao

2 comentarios:

angelborroy dijo...

Es la combinación de dos tendencias peligrosas: desde la parte de los frameworks simplificar la exposición de recursos complejos y desde la parte de los desarrolladores utilizar más tecnología de la necesaria (muchas veces sin entender realmente lo que están usando).

En cualquier caso, tampoco creo que la cosa sea tan catastrofista. XA es útil en ocasiones. Lo cierto es que yo únicamente he utilizado JTA para transacciones coordinadas a dos bases de datos distintas, pero el resultado ha sido satisfactorio. Y su utilización, a través del gestor de transacciones de Spring, ha resultado sencilla.

Server Performance dijo...

Te doy la razón en todo, no digo que XA no sea útil sino que puede ser un infierno en entornos heterogéneos o cuando han querido recubrir de XA cosas que es difícil que cuadren con el resto...

Por ejemplo el sistema de ficheros de los nuevos S.O. Windows es transaccional y podría ir en una XA: los accesos a disco pueden deshacerse y tienen sus commits y tal. Incluso creo que las nuevas versiones de JBoss incluyen un proveedor XA para acceso a ficheros.

Como dices, si sabes lo que estás haciendo (y muchas veces habría que haber estudiado mucho para eso) parece muy úitl, tiene una pinta estupenda.

Pero si en mitad de una transacción escribo a un sistema de log, bien en BDD mediante JDBC, bien asíncrono mediate JMS, bien en fichero... No quiero que eso forme parte de la transacción, quiero registrar cuando ha habido un error :-)))

La gran pregunta es: ¿por defecto todo complejo y manualmente decidimos excepciones? ¿o por defecto todo simple y el desarrollador decide cuándo utilizar escenarios complejos? Parece que la industria va por lo primero... :-(