Multi Session o Multi Transactions

Que tal, acudo a ustedes para llegar a la conclusión de que es lo más recomendable para atacar un problema que surgió recientemente.

Tenemos un sistema corriendo en jboss, el cual como sabran utiliza EJB's y estos mismos son las interfaces de servicio.

Tenemos ejecutandose un scheduler cada 10 minutos, y cuando detecta nueva información realiza un proceso especial que puede llegar a tardarse hasta 30 segundos. El detalle está en que como el lapso de tiempo es largo, algunas ocasiones los usuarios hacen movimientos con la información con la cual trabaja el scheduler la cual es rapidisima (menos de 1 segundo) por lo tanto el proceso del scheduler entra en acción el versioning configurado en hibernate y me rebota todo el proceso.

El problema es que la operacion es larga por que realiza varias pequeñas semioperaciones que deberian ser aisladas, pero como no contemplamos el problema que surgió orita, pues cada que termina una semioperacion notifica del exito via email, sin embargo si en la ultima semioperacion el proceso falla por el versioning, hace rollback a todo, pero nosotros ya notificamos de que algunas habian terminado correctamente, eh ahi el gran problema.

Entonces, como estamos utilizando jboss 4, en los EJB's inyectamos el entity manager, el session context y session factory. La idea que queremos utilizar es crear una nueva session de hibernate por cada semioperación con su respectiva transacción, y al finalizar hacer commit y si todo termina bien entonces se hace la notificación.

Pero lo que todavia no me queda muy claro, es que por ejemplo el metodo inició con un entity manager, una session de hibernate y una transaccion inicializada con la cual se trabaja. Si dentro del metodo llamo al sessionFactory.openSession() me crea una nueva session y a esta le inicio la transacción, la nueva sessión no esta ligada de alguna manera a la session original, donde si alguna de las nuevas sessiones creadas falla por lo del versioning me rebote todo otra vez? y en caso de que cada nueva session sea limpia y si pueda utilizar esto, no seria mejor usar solo una nueva sessión y por cada semioperacion crear una transacción (no es lo mismo)?, ahora si esto ultimo si aplica, puedo aplicarselo a la session original donde le llamo el metodo getTransacion y la termino, y voy creando nuevas transacciones por cada semioperacion?

Agradeceria cualquier respuesta para una mejor comprensión de mi parte.

Saludos

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

Me pregunto si es extraccion

Me pregunto si es extraccion de informacion de una DB a otra (o de una tabla a otra)

A: Registro Original
B: Registro Manipulado
C: Registro Procesado por el Scheduler

Veamos: el registro B es modificado (copia de A) y haces un commit a la DB pero C (que es otra copia de A) ya fue procesado y entonces tienes dos versiones diferentes de A (que es la B y la C). Ahora el problema es que ambas DB (o tablas o lo que sea) deben tener la misma informacion para que A == B por ende C== A pues...a mi se me ocurre añadir una lista de Updates disparadas al final del Scheduler para emparejar la informacion pero vuelves a caer en lo mismo (y si fue nuevamente modificado un dato?.... vaya problema. Segun recuerdo lo de versioning de Hibernate puedes consultar por version del registro certo?... a ver busquemos... Ya, mira esto en la parte de 11.3.1. Application version checking (oouch, odio que los subtitulos no tengan ancla).

Imagen de ezamudio

encolar avisos

No entendí bien qué solución buscan. Si lo que quieren es no avisar antes de tiempo porque el proceso puede tronar, lo que tienen que hacer es al principio crear una lista vacía con los avisos que se van a enviar al final; cada proceso parcial que termina bien, agrega su aviso a la lista... al final DESPUES del commit se envían todos los avisos que tenga la lista, sólo si todo salió bien; así no hay avisos prematuros de cosas que a la mera hora salieron mal.

Si lo que quieren es que NADIE le meta mano a los datos que procesa el superprocesador que se tarda un rato en hacer el superproceso, pues tendrían que bloquear tablas completas o todos los registros que piensan usar durante el proceso, manejar todo en una megatransacción y al final dar commit o rollback. Lo que entendí de esta parte es que la bronca está cuando un proceso ajeno (un usuario) modifica datos justo antes de que el superprocesador los lea, o usan el esquema de permitir que todo mundo lea algo pero justo antes de actualizar se dan cuenta que la versión del dato ya cambió porque hubo un update y entonces truena el subproceso y por lo tanto el superprocesador ya no pudo terminar bien. Para evitar eso tendrían que bloquear al principio todos los datos que piensan usar, pero tal vez no sea muy práctico eso. Creo que lo mejor es mandar los avisos hasta el final, sólo si nada truena; incluso manejar dos listas y que cada subproceso agregue dos mensajes a la lista; uno diciendo que todo salió bien y otro diciendo que salió mal, y al final si el commit sale bien, se envían todos los de la lista de OK, de lo contrario se envían todos los de la lista de error.

Boqueo de tablas??

Claro que ese antipatron afecta mucho para aplicaciones multiusuario.... pero regresando, si manejas los registros independientes no debe por queafectar toooodo... Aqui es cuando entra el Spring (no necesitas crear muiles de sesiones si ya tienes el sessionFactory instanciado como sigleton, solo haz transacciones) y asi procesas uno por uno, asi si una transaccion te genera conflicto (como dice @ezamudio) podras añadir el error a una lista de notificaciones que al final puedes enviar por ejemplo con jAlarms

Imagen de Nopalin

Semi-operaciones

El punto clave es que el proceso del scheduler procesa varias semi-operaciones, donde cada una debe ser aislada. Por ejemplo se procesa la semi-operacion 1, 2 y 3, pero por ejemplo la cuarta lee entidades para hacer el trabajo, en ese mismo instante despues de la leida un usuario modifica algun dato de estas entidades, entonces la version de la entidad que leyó la semi-operacion ya cambió, por lo tanto rebota todo haciendo rollback, incluidas las 3 semi-operaciones que si proceso bien.

Lo que queremos hacer es aislar cada semi-operacion, por ejemplo crear una sessin y una transacción por cada una y al final hacer commit y asegurarnos de que la información se guardó correctamente, asi cuando sigan las siguientes semi-operaciones, si alguna falla por versioning, ya solo se hace roolback en esa transaccion y las que si se procesaron bien se queden así. Lo de la notificación via email es un plus para el usuario pero no indispensable, aunque claro si se le notifica algo a un usuario este espera que sea verdad, por lo tanto creemos que es mejor no notificar y crear la info, que notificar y no crear nada (suponiendo un caso extremo claro está).

Esta es la solucion que tenemos, pero mi pregunta era mas para el lado de hibernate (no tanto de la lógica del proceso), si por ejemplo cuando entro a un metodo con una session padre, si creo otras sessiones hijos dentro de ésta, no se ligan de alguna forma? donde si alguna nueva sessión hijo falla, hace rollback hasta la sessión padre, y por ende hace rollback a todas las sessiones hijo que haya creado?

Suponiendo que las sesiones hijo son limpias y aisladas, es mejor crear una sola session hijo y dentro de esta crear una transacción por cada semi-operacion?, donde si alguna falla se hace rollback solo a la ultima transacción y las previas creadas hayan commiteado los datos

espero haberme hecho entender ahora.

Saludos

Imagen de ezamudio

currentSession

Si todos los componentes usan la misma SessionFactory de Hibernate, puedes invocar getCurrentSession(); ese método te da una sesión existente si ya la hay, o crea una nueva si no hay todavía. De ese modo ya no te preocupas de que si estas usando la misma o no.

Me imagino que lo que haces

Me imagino que lo que haces es como si hicieras un select dependiente de otro... por ejemplo

Scheduler
        |- SELECT A
                |- SELECT B
                        |- SELECT C
                                |- SELECT D

Editado: y si algun error sucede en D se cae el proceso de todos las transacciones anteriores no? (ah jijos, pus como lo haces?)
  

Si lo hicieras en Spring en el applicationContext habrias que definir tu bean de configuracion de Hibernate

        <bean id="sessionFactory"  class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" destroy-method="destroy">
                <property name="configLocation">
                        <value>/WEB-INF/hibernate.cfg.xml</value>
                </property>
        </bean>
 
 
 
        <bean id="CosaDao"  class="doo.daba.java.pruebas.db.postgres.dao.CosaDao">
                <property name="sessionFactory" ref="sessionFactoryPostgre"></property>
        </bean>

(por cierto tambien le "inyectaste la dependencia al DAO (es claro que debe tener una variable miembro de tipo SessionFactory llamada sessionFactory
  

Ahora para llamar la session desde el "Jaiberneit" debes hacerlo asi:

Session session = sessionFactoryPostgre.getCurrentSession ();

(porque el sessionFactory ya está instanciado (con el spring). Solo te interesa obtener una sesion activa (abiertapor supuesto)

  
Lo importante es que en tu DAO puedas tener un metodo somilar a este

public Csa select (int idCosa) {
       
        Session session = sessionFactory.getCurrentSession ();
        Transaction transaction = session.beginTransaction ();

        Cosa cosa = null;
       
        session.clear ();
        Query query = session.createQuery ("FROM Cosa where " ....
               
        try {
                cosa = (Cosa) query.uniqueResult();
        } catch (HibernateException he) {
                he.printStackTrace();
                transaction.rollback();
        } finally {
                session.flush();
                session.close();
        }

                return cosa;
}

  
De esa forma cada que ejecutas un SELECT (por ejemplo) estás aislando el error a solo esa transaccion

  
  


Editado:
Lo que no entiendo es lo de sesion hijo, padre... Me gustaria saber como lo haces para entender mas el conflicto (no he encontrado eso, y mira que ya voy hasta la pagina 8 de google)... a ver si te avientas un ejemplito rapido replicando la estructura que tienes. Estaria muy interesante eso porque no comprendo exactamente eso de Hijo/padre y de como puede una consulta tirar otras si son porcesos aislados
Imagen de Nopalin

Varias sessiones

Presisamente el punto es utilizar varias sesiones no solo una, para si falla una, no haga rollback a todo, si no lo pertinente a esta sessión.

Haber si me puedo explicar mejor.

Para empezar estoy utilizando jboss no spring. jboss es un EJB container, donde la especificacion indica que estos EJB's son beans de servicio (por decirlo a grandes razgos) similares a los BSI's que se definen en spring.
En spring uno define una interface y una implementación, y varia la forma en que se pueden exponer los servicios (puede ser rmi, httpinvoker, soa, etc). A cada bsi le inyectamos los dao's con los que va a trabajar y cada dao tienen una referencia al session factory de hibernate (o cualquier otra cosa pero no entremos en detalles) para poder crear sesiones y transacciones. En cada dao metodo del dao aplicas lo de obtener una sesion, crear una transaccion y asegurarte de hacer commit o rollback si falla. Esto como sabrán es muy pesado y por eso utilizando AOP se puede evitar. En spring y presisamente para hibernate uno puede definir un objeto HibernateTransactionManager, que cuando desde nuestro bsi llamamos a un metodo de un dao, este transaction manager entra en acción, crea la session, la transacción y nos evita todo el trabajo de andar haciendolo en cada metodo del dao. y asi todos felices y contentos.

En jboss la cosa cambia solo un poco, como ya dije en lugar de definir BSI's, se definen EJB's, donde automáticamente es expuesto atravez de rmi, pero no podemos llamarlo asi directamente, sino que tenemos que utilizar JNDI para obtenerlo. Pero de similar manera que el AOP en spring, cuando llamamos a un metodo de un EJB, el EJB-container (osea jboss) crea una session, una transaccion y nos la pone disponible para nuestra ejecución del metodo en el EJB, cuando termina la llamada al metodo, se hace commit o rollback dependiendo como termine.

Ahora despues de mi pobre explicación que seguramente ya conocían, lo que quiero es que una vez invocado un metodo de un EJB que es el que hace las semi-operaciones, y utilizando el sessionfactory que me es inyectado al ejb, crear nuevas sesiones de hibernate para manejar cada semi-operacion. Cuando digo session y padre y sessiones hijos me refiero al hecho de que estando dentro de una session hibernate (padre) mando a llamar nuevas sessiones (hijas).

llamada remota    jboss                               |   (todo esto se invoca desde la session P)                 |
------------->   |_____| --> inicializa session P --> | llama metodo del EJB          aqui por cada semi-operacion |
                             crea transaccion         | inyectando la session y   --> se crean nuevas sessiones de |
                                                      | session factory               hibernate (H)                |
                                                      _____________________________________________________________|
                                                      /                  |                         |                \      
                                                     /                   |                         |                 \
                                                    /                    |                         |                  \
                                                   /                     |                         |                   \
                                              Session H1             Session H2               Session H3              Session H4
                                        hace semi-operacion 1     hace semi-operacion 2    hace semi-operacion 3    hace semi-operacion 4
                                        termina y commitea       termina y commitea        termina y commitea       falla y hace rollback

Si las 4 semi operaciones las hiciera dentro de la session P, y la cuarta falla, se hace rollback a las 3 primeras, pero si hago cada una sessiones separadas hijas, yo espero que si falla la cuarta solo haga rollback en esa y las tres primeras no las toque.

Mis preguntas:

Si la nueva session hija creada tiene alguna relacion con la session padre, donde si la hija hace rollback, el padre tambien lo hace diciendole tambien a todas las demás hijas (que el escenario seria el mismito a si todo lo hago en la session padre). Si esto es así hay alguna forma de obtener una sessión limpia de hibernate?

En caso de que si pueda obtener una nueva session limpia, en lugar de crear una session con su transaccion por cada semi-operacion, es posible crear solo una session y con esta crear y terminar transacciones, una por cada semi-operacion?

Saludos y gracias por su tiempo.

Ah vaya!

Aun asi se me hace raro quete haga rollback a todas las transacciones que hagas sobre una sesion... porque justo el transaction.rollback();se hace por transaccion no or sesion.. bueno, aqui hay un ejemplo de como crear nuevas sesiones pero insisto en pensar que no debe ser necesario... En teoria puede funcionar, solo habriendo distrintas transacciones sobre la misma sesion

Imagen de ezamudio

getCurrentSession

Ya te dije, usa SessionFactory.getCurrentSession(). Lee la documentación: ese método crea una nueva sesión si no existe una ya abierta, y si ya existe una te devuelve esa. Pero cuando haces commit a una transacción, la siguiente vez que invoques getCurrentSession te dará una sesión diferente.

Y si tienes broncas de rollback o commit, podrías probar de usar savepoints.

Imagen de Nopalin

gracias por tu respuesta

gracias por tu respuesta ezamudio y presisamente es lo que quiero entender.

Cuando entro al metodo la primera vez, entra con una sesion abierta y una transaccion, si hago commit de esa transacción para crear nuevas transacciones, que pasaria cuando termine el metodo y se quiera hacer commit a una transacción que ya termine?

Mi otro problema que estoy tratando de encontrar respuestas sin hacer pruebas, creo que lo mejor para salir de dudas es a ensayo y error.

Saludos y gracias por su tiempo

Ya lo habiamos dicho

-.-' Por supuesto que debes hacer prueba y error... echale un lente de nuevo a esto