Uploaded image for project: 'JBoss Transaction Manager'
  1. JBoss Transaction Manager
  2. JBTM-3249

XA_RB* exception at prepare incorrectly still calls rollback.

    Details

    • Type: Bug
    • Status: Pull Request Sent (View Workflow)
    • Priority: Major
    • Resolution: Unresolved
    • Affects Version/s: 5.10.3.Final
    • Fix Version/s: None
    • Component/s: JTA
    • Labels:
      None
    • Steps to Reproduce:
      Hide
          @Test
          public void test() throws SecurityException, IllegalStateException, HeuristicMixedException, HeuristicRollbackException, SystemException, NotSupportedException, RollbackException {
      
              javax.transaction.TransactionManager tm = com.arjuna.ats.jta.TransactionManager
                      .transactionManager();
      
              tm.begin();
      
              javax.transaction.Transaction theTransaction = tm.getTransaction();
      
              assertTrue(theTransaction.enlistResource(new SimpleXAResource() {
      
                  @Override
                  public void rollback(Xid xid) throws XAException {
                      resource1Rollback = true;
                  }
              }));
      
              assertTrue(theTransaction.enlistResource(new SimpleXAResource() {
      
                  @Override
                  public int prepare(Xid xid) throws XAException {
                      throw new XAException(XAException.XA_RBINTEGRITY);
                  }
      
                  @Override
                  public void rollback(Xid xid) throws XAException {
                      resource2Rollback = true;
                  }
              }));
      
              try {
                  tm.commit();
                  fail("Should not have committed");
              } catch (RollbackException e) {
                  assertTrue(resource1Rollback);
                  assertFalse(resource2Rollback);
              }
          }
      
      Show
      @Test public void test() throws SecurityException, IllegalStateException, HeuristicMixedException, HeuristicRollbackException, SystemException, NotSupportedException, RollbackException { javax.transaction.TransactionManager tm = com.arjuna.ats.jta.TransactionManager .transactionManager(); tm.begin(); javax.transaction.Transaction theTransaction = tm.getTransaction(); assertTrue(theTransaction.enlistResource( new SimpleXAResource() { @Override public void rollback(Xid xid) throws XAException { resource1Rollback = true ; } })); assertTrue(theTransaction.enlistResource( new SimpleXAResource() { @Override public int prepare(Xid xid) throws XAException { throw new XAException(XAException.XA_RBINTEGRITY); } @Override public void rollback(Xid xid) throws XAException { resource2Rollback = true ; } })); try { tm.commit(); fail( "Should not have committed" ); } catch (RollbackException e) { assertTrue(resource1Rollback); assertFalse(resource2Rollback); } }

      Description

      XAResourceRecord.topLevelPrepare maps XA_RB* codes as PREPARE_NOTOK, which causes ArjunaCore to re-queue the record on the pending list and subsequently call topLevelAbort. This is incorrect from an XA spec perspective, as XA_RB at prepare indicates the RM has terminated and forgotten the tx, so a rollback thereafter may causes unexpected problems.

      Whereas the elegant fix is probably a new TwoPhaseOutcome to distinguish 'NOTOK, don't call me again' from 'NOTOK, roll me back', that's a bit of a pain. The alternative is to leverage the mechanism we already have for employing this optimization in cases where we can, and extend it to cases where we should.

      			switch (e1.errorCode) {
      				case XAException.XA_RBROLLBACK:
      				case XAException.XA_RBEND:
      				case XAException.XA_RBCOMMFAIL:
      				case XAException.XA_RBDEADLOCK:
      				case XAException.XA_RBINTEGRITY:
      				case XAException.XA_RBOTHER:
      				case XAException.XA_RBPROTO:
      				case XAException.XA_RBTIMEOUT:
      // we may want to pull XAER_NOTA up here too?
      					if (_theTransaction != null) {
      						_theTransaction.setXAResourceState(_theXAResource, TxInfo.OPTIMIZED_ROLLBACK);
      // in a perfect word we'd call removeConnection here, but we probably need to do in in topLevelAbort instead, unless we change its implementation
      					}
      // now fall through...
      				case XAException.XAER_RMERR:
      				case XAException.XAER_RMFAIL:
      				case XAException.XAER_INVAL:
      				case XAException.XAER_PROTO:
      				case XAException.XAER_NOTA: // resource may have arbitrarily rolled back (shouldn't, but ...)
      					return TwoPhaseOutcome.PREPARE_NOTOK;  // will not call rollback [*cough*bullshit*cough*]
      				default:
      					return TwoPhaseOutcome.HEURISTIC_HAZARD; // we're not really sure (shouldn't get here though).
      			}
      

        Gliffy Diagrams

          Attachments

            Activity

              People

              • Assignee:
                mmusgrov Michael Musgrove
                Reporter:
                jhalliday Jonathan Halliday
              • Votes:
                0 Vote for this issue
                Watchers:
                3 Start watching this issue

                Dates

                • Created:
                  Updated: