TX その25 完成
まあシリーズ化して25話まで続けてきたJBossTXネタですが,ここらで御しまい.作ろうとしていたものができあがったから.
できあがったものをちょっとだけ公開.例えばJBossからXAを使ってMySQLへ2本コネクションを張っていたとする.Commitする直前で片方のコネクションで障害が発生した場合,Commit呼び出しであったとしても全体としてはRollbackされないといけない.例えばMySQLのGeneral Logで言うならば,以下のようなログにならないといけない.
69 Query XA START 0x6d697234322f35,0x31,0x101 70 Query XA START 0x6d697234322f35,0x32,0x101 69 Query INSERT INTO t1 VALUES(1) 70 Query INSERT INTO t2 VALUES(1) 71 Query SELECT COUNT(*) FROM t1 71 Query SELECT COUNT(*) FROM t2 70 Query SELECT connection_id() 71 Query KILL 70 69 Query XA END 0x6d697234322f35,0x31,0x101 70 Query XA END 0x6d697234322f35,0x32,0x101 69 Query XA PREPARE 0x6d697234322f35,0x31,0x101 69 Query XA ROLLBACK 0x6d697234322f35,0x31,0x101 71 Query SELECT COUNT(*) FROM t1 71 Query SELECT COUNT(*) FROM t2
ID=69とID=70のコネクションがXAを使っているコネクション.ID=71はテスト結果を確認したりするのに使っているコネクション.
ID=70がCommitの直前でKILLされている.ID=69はCommit命令により"XA PREPARE"を行うが,全体としては失敗なため"XA COMMIT"ではなく"XA ROLLBACK"をその後行うわけで.
そしてこれは,以下のコードで再現できる.
public void testErrorAtGroupCommit() throws Exception { MysqlXADataSource xads = new MysqlXADataSource(); XAConnection xaConn = xads.getXAConnection(); XAResource xaResource = xaConn.getXAResource(); Statement xaStmt = xaConn.getConnection().createStatement(); XAConnection xaConn2 = xads.getXAConnection(); XAResource xaResource2 = xaConn2.getXAResource(); Statement xaStmt2 = xaConn2.getConnection().createStatement(); Connection testConn = getConnectionWithProps(null); Statement testStmt = testConn.createStatement(); testStmt.executeUpdate("DROP TABLE IF EXISTS t1"); testStmt.executeUpdate("CREATE TABLE t1 (c1 int) ENGINE = InnoDB"); testStmt.executeUpdate("DROP TABLE IF EXISTS t2"); testStmt.executeUpdate("CREATE TABLE t2 (c1 int) ENGINE = InnoDB"); TxManager tm = TxManager.getInstance(); tm.begin(); Transaction tx = tm.getTransaction(); tx.enlistResource(xaResource); tx.enlistResource(xaResource2); xaStmt.executeUpdate("INSERT INTO t1 VALUES(1)"); xaStmt2.executeUpdate("INSERT INTO t2 VALUES(1)"); // check the table data before trying to commit ResultSet rs = testStmt.executeQuery("SELECT COUNT(*) FROM t1"); rs.next(); assertEquals(0, rs.getInt(1)); rs = testStmt.executeQuery("SELECT COUNT(*) FROM t2"); rs.next(); assertEquals(0, rs.getInt(1)); rs = xaStmt2.executeQuery("SELECT connection_id()"); rs.next(); int id = rs.getInt(1); testStmt.executeUpdate("KILL " + id); try { tm.commit(); fail(); } catch (JBossRollbackException ex) { // ok if RollbackException is thrown } // check if transaction is rollbacked rs = testStmt.executeQuery("SELECT COUNT(*) FROM t1"); rs.next(); assertEquals(0, rs.getInt(1)); rs = testStmt.executeQuery("SELECT COUNT(*) FROM t2"); rs.next(); assertEquals(0, rs.getInt(1)); xaStmt.close(); xaStmt2.close(); testStmt.close(); xaConn.close(); xaConn2.close(); testConn.close(); }
そういうことで,御しまい.