3 Replies Latest reply on May 16, 2007 11:47 AM by adrian.brock

    Force Oracle to Rollback _after_ prepare phase?

    ypsilon

      Hi

      is it possible to make JBoss force oracle to roll a transaction back in the commit phase? The problem here is that once in a while it occurs that one of the resources fails to respond to the commit request even if the prepare call succeeded just seconds ago. Since in most of the cases this means that the questionable resource did not commit it would be helpful to roll the other participant in the transaction (= Oracle 10g) back.

      Is there any way to achieve this? Far as I know it's up to the transaction manager to make such a decision. Can the JBoss transaction manager be forced to make this decision? (Actually I do not even know if oracle can do this, but I would bet so)

      If not: Are there any other options than using the LastResource interface for the unreliable resource?

      Thanks in advance for any help.

      Greetings
      PeeR

        • 1. Re: Force Oracle to Rollback _after_ prepare phase?
          weston.price

          This is a heuristic.

          Technically once a resource agrees in the prepare phase, the commit must occur. Is the offensive resource throwing an Exception in your case? This should force the rollback of the other participant. When you say 'does not respond to the commit request' what do you mean?

          • 2. Re: Force Oracle to Rollback _after_ prepare phase?
            ypsilon

            Hi Weston.


            "weston.price@jboss.com" wrote:
            This is a heuristic.

            Technically once a resource agrees in the prepare phase, the commit must occur. Is the offensive resource throwing an Exception in your case? This should force the rollback of the other participant. When you say 'does not respond to the commit request' what do you mean?


            The problem is that the resource seems to be no longer available. The TCP-Socket times out. This of course means that the commit request might have reached it's destination and thereby caused the commit to be executed, but from my experiences so far this did not happen.
            Since all communication is done via HTTP and the ResourceManager itself is not java an Exception is not really possible. In case of a timeout the XAResource at the moment throws a XAException (RMERR).

            If I however set up a test case and throw whatever exception in the XAResource the other participant is not being rolled back. Here is some sample code:

            The Method which does the work looks like this (called remotely):
            
             /**
             * @throws CreateException
             * @ejb.interface-method
             * @ejb.transaction type="Required"
             */
             public void jtaTest() throws RemoteException, CreateException {
            
            ctx.lookup("java:/TransactionManager");
             try {
             TransactionManager transactionManager = (TransactionManager)new InitialContext().lookup("java:/TransactionManager");
             log.info(transactionManager.getClass());
             transactionManager.getTransaction().enlistResource(new MyXAResource());
             AddressValue addy2 = new AddressValue();
             addy2.setCity("einestadt");
             addy2.setId(new Long(System.currentTimeMillis()));
             addy2.setName("ich");
             addy2.setStreet("am kamp 12");
             AddressUtil.getLocalHome().create(addy2) ;
            
             } catch (Exception e) {
             e.printStackTrace();
             ctx.setRollbackOnly();
             }
             }
            
            
            And this is the enlisted XAResource:
            
             private static class MyXAResource implements XAResource {
             Xid managedXid = null;
            
             public void commit(Xid xid, boolean b) throws XAException {
             log.info("Nummer 1:commiting " + xid);
             int testFlag=0;
             switch (testFlag) {
             case 0:
            
             log.info("Nummer 1:Error during commit");
             throw new XAException(XAException.XA_HEURRB);
             case 1:
            
             log.info("Nummer 1:Error during commit");
             throw new RuntimeException("blabla");
             case 2:
            
             log.info("Nummer 1:Error during commit");
             throw new XAException(XAException.XAER_RMERR);
            
             }
            
             log.info("Nummer 1:done commiting");
             }
            
             public void end(Xid xid, int i) throws XAException {
             log.info("Nummer 1:end transcation " + xid);
             }
            
             public void forget(Xid xid) throws XAException {
             log.info("Nummer 1:forget transcation " + xid);
             }
            
             public int getTransactionTimeout() throws XAException {
             return 5000; //To change body of implemented methods use File | Settings | File Templates.
             }
            
             public boolean isSameRM(XAResource xaResource) throws XAException {
             log.info("Nummer 1:isSameRM called");
             return xaResource == this;
             }
            
             public int prepare(Xid xid) throws XAException {
             boolean flag = false;
             log.info("Nummer 1: prepare called.");
             if (flag) {
             throw new XAException(XAException.XA_RBCOMMFAIL );
             }
            
             return XA_OK; //To change body of implemented methods use File | Settings | File Templates.
             }
            
             public Xid[] recover(int i) throws XAException {
             log.info("Nummer 1:recover called");
             return new Xid[0]; //To change body of implemented methods use File | Settings | File Templates.
             }
            
             public void rollback(Xid xid) throws XAException {
             boolean flag = false;
             if (flag) {
             throw new XAException(XAException.XAER_RMERR );
             }
             }
            
             public boolean setTransactionTimeout(int i) throws XAException {
             return false; //To change body of implemented methods use File | Settings | File Templates.
             }
            
             public void start(Xid xid, int flags) throws XAException {
             if (xid == null) throw new XAException(XAException.XAER_INVAL);
             if (managedXid != null) {
             throw new XAException(XAException.XAER_INVAL);
             } else {
             managedXid = xid;
             }
             log.info("Nummer 1:start called " + xid + " i " +flags);
             }
             }
            
            


            as you can see in commit there is always an XAException being thrown.

            here is some logging

            11:40:15,195 INFO [CMRTestFacadeBean] Nummer 1:Error during commit
            11:40:15,196 WARN [TransactionImpl] XAException: tx=TransactionImpl:XidImpl[FormatId=257, GlobalId=toaster/15, BranchQual=, localId=15] errorCode=XA_HEURRB
            javax.transaction.xa.XAException
            at eval.cmr.CMRTestFacadeBean$MyXAResource.commit(CMRTestFacadeBean.java:313)action.xa.XAException
            at org.jboss.tm.TransactionImpl$Resource.commit(TransactionImpl.java:2253)
             at org.jboss.tm.TransactionImpl.commitResources(TransactionImpl.java:1784)
             at org.jboss.tm.TransactionImpl.commit(TransactionImpl.java:358)
            ...
            "weston.price@jboss.com" wrote:
            This is a heuristic.
            
            Technically once a resource agrees in the prepare phase, the commit must occur. Is the offensive resource throwing an Exception in your case? This should force the rollback of the other participant. When you say 'does not respond to the commit request' what do you mean?
            
            
            The problem is that the resource seems to be no longer available. The TCP-Socket times out. This of course means that the commit request might have reached it's destination and thereby caused the commit to be executed, but from my experiences so far this did not happen.
            Since all communication is done via HTTP and the ResourceManager itself is not java an Exception is not really possible.
            
            If I however set up a test case and throw whatever exception in the XAResource the other participant is not being rolled back. Here is some sample code:
            
            
            The Method which does the work looks like this (called remotely):
            
             /**
             * @throws CreateException
             * @ejb.interface-method
             * @ejb.transaction type="Required"
             */
             public void jtaTest() throws RemoteException, CreateException {
            
            ctx.lookup("java:/TransactionManager");
             try {
             TransactionManager transactionManager = (TransactionManager)new InitialContext().lookup("java:/TransactionManager");
             log.info(transactionManager.getClass());
             transactionManager.getTransaction().enlistResource(new MyXAResource());
             AddressValue addy2 = new AddressValue();
             addy2.setCity("einestadt");
             addy2.setId(new Long(System.currentTimeMillis()));
             addy2.setName("ich");
             addy2.setStreet("am kamp 12");
             // save the address to the DB
             AddressUtil.getLocalHome().create(addy2) ;
            
             } catch (Exception e) {
             e.printStackTrace();
             ctx.setRollbackOnly();
             }
             }
            
            
            And this is the enlisted XAResource:
            
             private static class MyXAResource implements XAResource {
             Xid managedXid = null;
            
             public void commit(Xid xid, boolean b) throws XAException {
             log.info("Nummer 1:commiting " + xid);
             int testFlag=0;
             switch (testFlag) {
             case 0:
            
             log.info("Nummer 1:Error during commit");
             throw new XAException(XAException.XA_HEURRB);
             case 1:
            
             log.info("Nummer 1:Error during commit");
             throw new RuntimeException("blabla");
             case 2:
            
             log.info("Nummer 1:Error during commit");
             throw new XAException(XAException.XAER_RMERR);
            
             }
            
             log.info("Nummer 1:done commiting");
             }
            
             public void end(Xid xid, int i) throws XAException {
             log.info("Nummer 1:end transcation " + xid);
             }
            
             public void forget(Xid xid) throws XAException {
             log.info("Nummer 1:forget transcation " + xid);
             }
            
             public int getTransactionTimeout() throws XAException {
             return 5000; //To change body of implemented methods use File | Settings | File Templates.
             }
            
             public boolean isSameRM(XAResource xaResource) throws XAException {
             log.info("Nummer 1:isSameRM called");
             return xaResource == this;
             }
            
             public int prepare(Xid xid) throws XAException {
             boolean flag = false;
             log.info("Nummer 1: prepare called.");
             if (flag) {
             throw new XAException(XAException.XA_RBCOMMFAIL );
             }
            
             return XA_OK; //To change body of implemented methods use File | Settings | File Templates.
             }
            
             public Xid[] recover(int i) throws XAException {
             log.info("Nummer 1:recover called");
             return new Xid[0]; //To change body of implemented methods use File | Settings | File Templates.
             }
            
             public void rollback(Xid xid) throws XAException {
             boolean flag = false;
             if (flag) {
             throw new XAException(XAException.XAER_RMERR );
             }
             }
            
             public boolean setTransactionTimeout(int i) throws XAException {
             return false; //To change body of implemented methods use File | Settings | File Templates.
             }
            
             public void start(Xid xid, int flags) throws XAException {
             if (xid == null) throw new XAException(XAException.XAER_INVAL);
             if (managedXid != null) {
             throw new XAException(XAException.XAER_INVAL);
             } else {
             managedXid = xid;
             }
             log.info("Nummer 1:start called " + xid + " i " +flags);
             }
             }
            
            
            
            
            as you can see in commit there is always an XAException being thrown.
            
            here is some logging
            
            
            11:40:15,195 INFO [CMRTestFacadeBean] Nummer 1:Error during commit
            11:40:15,196 WARN [TransactionImpl] XAException: tx=TransactionImpl:XidImpl[FormatId=257, GlobalId=toaster/15, BranchQual=, localId=15] errorCode=XA_HEURRB
            javax.transaction.xa.XAException
            at eval.cmr.CMRTestFacadeBean$MyXAResource.commit(CMRTestFacadeBean.java:313)action.xa.XAException
            at org.jboss.tm.TransactionImpl$Resource.commit(TransactionImpl.java:2253)
             at org.jboss.tm.TransactionImpl.commitResources(TransactionImpl.java:1784)
             at org.jboss.tm.TransactionImpl.commit(TransactionImpl.java:358)
            ...
            11:40:15,209 INFO [CMRTestFacadeBean] Nummer 1:forget transcation XidImpl[FormatId=257, GlobalId=toaster/15, BranchQual=1, localId=15]
            11:40:15,242 ERROR [LogInterceptor] TransactionRolledbackException in method: public abstract void eval.cmr.CMRTestFacade.jtaTest() throws javax.ejb.CreateException,java.rmi.RemoteException, causedBy:
            javax.transaction.HeuristicRollbackException
             at org.jboss.tm.TransactionImpl.checkHeuristics(TransactionImpl.java:1610)
             at org.jboss.tm.TransactionImpl.commit(TransactionImpl.java:378)
             at org.jboss.ejb.plugins.TxInterceptorCMT.endTransaction(TxInterceptorCMT.java:501)
             at org.jboss.ejb.plugins.TxInterceptorCMT.runWithTransactions(TxInterceptorCMT.java:361)
             at org.jboss.ejb.plugins.TxInterceptorCMT.invoke(TxInterceptorCMT.java:181)
             at org.jboss.ejb.plugins.SecurityInterceptor.invoke(SecurityInterceptor.java:168)
            
            
            
            
            
            The address which is created is however being written to the Database.
            
            Is that normal?
            
            Thanks in advance
            Greetings
            PeeR
            


            • 3. Re: Force Oracle to Rollback _after_ prepare phase?

               

              "ypsilon" wrote:

              The address which is created is however being written to the Database.

              Is that normal?


              Yes that is normal. Heuristics are bad. It means you have an inconsistent commit.

              To fix this problem (commit phase fails after prepare)
              you need a recovering TM, NOT heuristics, that will
              remember the failed commit phase and try the request again "later"
              when it has re-established the connectivity.
              http://labs.jboss.com/jbosstm/