Search code examples
javamysqltransactionsjtaxa

JTA XAResource multiple databases: what if two phase commit fails after prepare


I need to do a POC which uses JTA XA Resource with MySQL in a core java application (not web), without using any framework. All the articles and examples show commit using single datasource. Although I've done distributed transaction with two databases, but I've doubts/queries in 'Two Phase Commit' using XA Resource. My Code is:

 public static void main(String[] args) {
    DataSource dataSourceRemote = ConnectionManager.getDatasourceRemote();
    DataSource dataSourceLocal = ConnectionManager.getDatasourceLocal();
XAResource xaRes;
    XAResource xaRes2; 
    XID xid, xid2;
    try {

        XADataSource xaDataSourceLocal;
        XAConnection xaCon;

        //XID xid;
        Connection con;
        Statement stmt;
        int ret;

        XADataSource xaDataSourceRemote;
        XAConnection xaCon2;

        //XID xid2;
        Connection con2;
        Statement stmt2;
        int ret2;xaDataSourceLocal = (XADataSource) dataSourceLocal;

        xaCon = xaDataSourceLocal.getXAConnection("root", "root");
        con = xaCon.getConnection();
        stmt = con.createStatement();
        xaRes = xaCon.getXAResource();

        xaDataSourceRemote = (XADataSource) dataSourceRemote;

        xaCon2 = xaDataSourceRemote.getXAConnection("root", "root");
        con2 = xaCon2.getConnection();
        stmt2 = con2.createStatement();
        xaRes2 = xaCon2.getXAResource();

        con.setAutoCommit(false);
        con2.setAutoCommit(false);

        xid = new XID(100, new byte[] {0x01}, new byte[] {0x02});
        xid2 = new XID(101, new byte[] {0x02}, new byte[] {0x03});

        xaRes.start(xid, XAResource.TMNOFLAGS);
        String query = "insert into emp values (12, \"Sanjay\", \"12345\",  100000)";
        System.out.println(query);
        stmt.executeUpdate(query);


        xaRes2.start(xid2, XAResource.TMNOFLAGS);
        stmt2.executeUpdate("insert into emp values (11, \"Nikhil\", \"12345\",        100000)");


        xaRes2.end(xid2, XAResource.TMSUCCESS);
        xaRes.end(xid, XAResource.TMSUCCESS);ret = xaRes.prepare(xid);
        ret2 = xaRes2.prepare(xid2);

        if(ret == XAResource.XA_OK && ret2 == XAResource.XA_OK){ 
            xaRes2.commit(xid2, false);
            xaRes.commit(xid, false);

        }
        con.close();
        con2.close();
        xaCon.close();
        xaCon2.close();

        con2.close();
     } catch (XAException e) {

        e.printStackTrace();            
    } 
    catch (SQLException e) {

        e.printStackTrace();
    }
}

}

Now My queries are:

1) Is there any need to define two Xids as if I replace Xid2 with Xid, code runs fine

2) I've took debug at line : if(ret == XAResource.XA_OK && ret2 == XAResource.XA_OK), when prepare returns XAResource.XA_OK and condition got true for both the xresources. Now if I stop MySQL service to my local machine, xaRes2.commit(xid2, false); would run successfully and xaRes.commit(xid, false); would throw XA Exception. In this case 'two phase commit' fails, as one datasource is in inconsistent state. My question is, am I doing it right or is there other way of doing it?

3) What is the actual use of recover, as in this case one transaction has got commited and other failed after prepare returned successfully


Solution

  • 1) I think you got confused. An XA transaction is identified/referenced by an XID. So if you use two xID's, you will have two XA transactions.

    You can use the same XID in two different jam's and they both can be part of the same XA transaction.

    2) Generally you have a transaction manager that takes care of this and the cleanup in case of error. In this case you basically need to implement this and you need to take care of all the error cases, which is not trivial. To make sure the recovery part works correctly, you need to test all the different error scenarios. Oracle for instance allows you to fail the commit at different points in the commit code path for testing. I'm not sure mysql has the same ability.

    3) Recovery is required for instance if one of the branches of your XA transaction fails, or if you commit and one of your resource managers cannot commit.

    In general if you use a database like Mysql in your case, you need the database to implement the xa protocol, as otherwise recovery will most likely not work correctly. Of course you will need to tell the database, that this is an xa transaction.