제5장 분산 트랜잭션

내용 목차

5.1. 개요
5.2. 구성요소
5.3. 전역 트랜잭션과 지역 트랜잭션 간의 변환
5.4. Tibero XA 패키지
5.5. XA 인터페이스의 구성요소
5.5.1. XADataSource 인터페이스
5.5.2. XAConnection 인터페이스
5.5.3. XAResource 인터페이스
5.5.4. XID 인터페이스
5.6. 분산 트랜잭션 예제

본 장에서는 tbJDBC에서 제공하는 분산 트랜잭션(Distributed Transaction) 기능을 설명한다. 분산 트랜잭션의 대표적인 예로는 XA(Extended Architecture)가 있으며 이와 관련된 설명을 주로 한다.

분산 트랜잭션은 보통 전역 트랜잭션이라고도 하는데, 통합으로 관리되는 하나 이상의 트랜잭션 집합을 말한다. 분산 트랜잭션에 참여하는 트랜잭션은 동일한 데이터베이스에 존재할 수도 있고, 다른 데이터베이스에 존재할 수도 있다. 이러한 각각의 트랜잭션을 트랜잭션 브랜치(Transaction Branch)라고 한다.

분산 트랜잭션은 JDBC 2.0 표준의 확장 API로서 접속 풀링 기능을 기반으로 제공되거나 X/Open DTP(Distributed Transaction Processing) 규약의 XA 표준으로 제공되기도 한다.

분산 트랜잭션은 다음과 같은 특징이 있다.

다음은 XA 구성요소별 기능 설명이다.

JDBC 3.0 표준에서는 전역 트랜잭션과 지역 트랜잭션 사이에서 커넥션을 공유할 수 있고, 각각으로 변환할 수 있다.

일반적으로 커넥션은 다음의 세 가지 모드 중에 반드시 하나를 갖는다.

모드설명
NO_TXN현재 커넥션을 사용하는 활성화된 트랜잭션이 없는 모드이다.
LOCAL_TXN

auto-commit 모드를 비활성화시킨 상태로 현재 커넥션을 사용하는 활성화된 트랜잭션이 있는 모드이다.

커넥션 모드에 따라 prepare(), commit(), rollback(), forget(), end() 메소드를 호출할 수 없고, XAException이 발생한다.

GLOBAL_TXN

현재 커넥션을 사용하는 활성화된 트랜잭션이 있는 모드이다.

커넥션 모드에 따라 commit(), rollback(), setAutoCommit(), setSavepoint() 메소드를 호출할 수 없고, SQLException이 발생한다.

각 커넥션은 실행 상태에 따라 다음과 같이 세 가지 모드 사이에서 자동으로 바뀐다. 단, 커넥션이 초기화될 때에는 항상 NO_TXN 모드로 동작한다.

현재 모드NO_TXN으로 변환LOCAL_TXN으로 변환GLOBAL_TXN으로 변환
NO_TXN-auto-commit 모드를 비활성화시키고 DML 문을 수행했을 때XAConnection에서 얻은 XAResource에 end() 메소드를 호출했을 때
LOCAL_TXNDDL 문이 수행되거나 commit() 또는 rollback() 메소드를 호출했을 때-불가능
GLOBAL_TXNXAConnection에서 얻은 XAResource에 end() 메소드를 호출했을 때불가능-

Tibero에서는 XA 표준에 따른 분산 트랜잭션 패키지인 com.tmax.tibero.jdbc.ext 내부에 다음과 같은 클래스를 지원한다.

  • TbXAConnection

  • TbXADataSource

  • TbXAException

  • TbXAResource

  • TbXid

본 절에서는 JDBC 2.0 표준 패키지에 정의된 XA 인터페이스의 구성요소를 설명한다.

javax.transaction.xa.XAResource에 정의된 인터페이스는 다음과 같다.

public interface XAResource
{
  void commit(Xid xid, boolean onePhase) throws XAException;
  void end(Xid xid, int flags) throws XAException;
  void forget(Xid xid) throws XAException;
  int prepare(Xid xid) throws XAException;
  int rollback(Xid xid) throws XAException;
  void start(Xid xid, int flags) throws XAException;
  boolean isSameRM(XAResource xares) throws XAException;
}

tbJDBC에서는 com.tmax.tibero.jdbc.ext.TbXAResource 클래스로 제공된다.

XAResource 인터페이스에서 설정할 수 있는 메소드는 다음과 같다.

메소드설명
StartXID와 관련된 트랜잭션의 특정 브랜치를 시작하거나 이미 존재하는 트랜잭션을 재시작 또는 변경 사항을 조인한다.
EndXID와 관련된 트랜잭션의 특정 브랜치에 대한 종료 상태(정상 또는 실패)를 알리거나 이미 존재하는 트랜 잭션을 멈춘다.
Prepare현재의 트랜잭션 브랜치에서 수행된 변경사항을 적용하기 위해 준비한다.
Commit현재의 트랜잭션 브랜치의 변화를 반영한다.
Rollback현재의 트랜잭션 브랜치의 변화를 롤백시킨다.
Forget리소스 매니저가 지정한 트랜잭션 브랜치를 무시하도록 한다.
Recover현재 준비가 완료되었거나 수행이 끝난 트랜잭션 브랜치의 목록을 반환한다.
isSameRM두 개의 XA 리소스 객체가 동일한 리소스 매니저에 속해 있는지의 여부를 반환한다.

Start

XID와 관련된 트랜잭션의 특정 브랜치를 시작하거나 이미 존재하는 트랜잭션을 재시작 또는 변경 사항을 조인시키기 위해 사용하는 메소드이다.

  • 문법

    void start(Xid xid, int flags)
  • 파라미터

    파라미터설명
    xid현재 리소스 객체와 관련된 글로벌 트랜잭션의 ID이다.
    flags

    flags 파라미터는 반드시 다음의 값 중의 하나여야 한다.

    • XAResource.TMNOFLAGS: 새로운 트랜잭션 브랜치가 시작된다.

    • XAResource.TMJOIN: 이미 존재하는 트랜잭션 브랜치에 다음 동작을 조인시킨다.

    • XAResource.TMRESUME: 정지된 트랜잭션을 다시 시작시킨다.

    • TbXAResource.TBRTMSERIALIZABLE: Serializable 트랜잭션을 시작시킨다.

    • TbXAResource.TBRTMREADONLY: Read-only 트랜잭션을 시작시킨다.

    • TbXAResource.TBRTMREADWRITE: Read/write 트랜잭션을 시작시킨다.

    • TbXAResource.TBRTRANSLOOSE: Loosely-coupled 트랜잭션을 시작시킨다.

End

XID와 관련된 트랜잭션의 특정 브랜치에 대한 종료 상태(정상 또는 실패)를 알리거나 이미 존재하는 트랜잭션을 멈추기 위해 사용하는 메소드이다.

  • 문법

    void end(Xid xid, int flags)
  • 파라미터

    파라미터설명
    xid현재 리소스 객체와 관련된 글로벌 트랜잭션의 ID이다.
    flags

    flags 파라미터는 반드시 다음의 값 중의 하나여야 한다.

    • XAResource.TMSUCCESS: 현재 트랜잭션 브랜치가 성공했음을 알린다.

    • XAResource.TMFAIL: 현재 트랜잭션 브랜치가 실패했음을 알린다.

    • XAResource.TMSUSPEND: 현재 트랜잭션 브랜치를 정지시킨다.

Prepare

현재의 트랜잭션 브랜치에서 수행된 변경사항을 적용하기 위해 준비하는 과정으로 Two-phase commit 과정의 첫 번째 단계이다. 만약 분산 트랜잭션 내부에 오직 하나의 트랜잭션만 있는 경우라면 prepare()를 수행할 필요가 없다.

  • 문법

    int prepare(Xid xid)
  • 파라미터

    파라미터설명
    xid현재 리소스 객체와 관련된 글로벌 트랜잭션의 ID이다.
  • 반환 값

    이 메소드는 반드시 다음의 값 중의 하나를 반환한다.

    반환 값설명
    XAResource.XA_RDONLY현재의 트랜잭션 브랜치는 read-only 모드로 동작하므로 SELECT 문만 수행할 수 있다.
    XAResource.XA_OK현재의 트랜잭션 브랜치에서는 어떠한 수정 작업도 수행할 수 있다.

Commit

현재의 트랜잭션 브랜치의 변화를 반영하는 과정으로 Two-phase commit 과정의 두 번째 단계이다. 단, 모든 트랜잭션 브랜치가 prepare를 완료한 후에 수행된다.

  • 문법

    void commit(Xid xid, boolean isOnePhase)
  • 파라미터

    파라미터설명
    xid현재 리소스 객체와 관련된 글로벌 트랜잭션의 ID이다.
    isOnePhase

    isOnePhase 파라미터에 설정된 값에 따라 다음과 같이 동작한다.

    • true: Two-phase commit 과정이 아닌 One-phase commit 과정으로 동작한다.

    • false: Two-phase commit 과정으로 동작한다.

Rollback

현재의 트랜잭션 브랜치의 변화를 롤백시킨다.

  • 문법

    void rollback(Xid xid)
  • 파라미터

    파라미터설명
    xid현재 리소스 객체와 관련된 글로벌 트랜잭션의 ID이다.

Forget

리소스 매니저가 지정한 트랜잭션 브랜치를 무시하도록 한다.

  • 문법

    void forget(Xid xid)
  • 파라미터

    파라미터설명
    xid현재 리소스 객체와 관련된 글로벌 트랜잭션의 ID이다.

Recover

현재 준비가 완료되었거나 수행이 끝난 트랜잭션 브랜치의 목록을 반환한다.

  • 문법

    Xid[] recover(int flag)
  • 파라미터

    파라미터설명
    flag

    flags 파라미터는 반드시 다음의 값 중의 하나여야 한다.

    • XAResource.TMSTARTSCAN: 현재 준비가 완료된 트랜잭션을 반환한다.

    • XAResource.TMENDSCAN: 현재 수행이 끝난 트랜잭션을 반환한다.

    • XAResource.TMNOFLAGS: 모든 트랜잭션을 반환한다.

isSameRM

두 개의 XA 리소스 객체가 동일한 리소스 매니저에 속해 있는지의 여부를 반환한다.

  • 문법

    boolean isSameRM(XAResource aResource)
  • 파라미터

    파라미터설명
    aResource현재 리소스 객체와 비교할 리소스 객체이다.

다음은 두 개의 트랜잭션 브랜치로 이루어진 Two-phase 분산 트랜잭션 환경을 구현하는 순서이다.

  1. 트랜잭션 브랜치 #1을 시작한다.

  2. 트랜잭션 브랜치 #2를 시작한다.

  3. 트랜잭션 브랜치 #1에서 DML 문장을 수행한다.

  4. 트랜잭션 브랜치 #2에서 DML 문장을 수행한다.

  5. 트랜잭션 브랜치 #1의 트랜잭션을 종료한다.

  6. 트랜잭션 브랜치 #2의 트랜잭션을 종료한다.

  7. 트랜잭션 브랜치 #1을 준비한다.

  8. 트랜잭션 브랜치 #2를 준비한다.

  9. 트랜잭션 브랜치 #1을 커밋한다.

  10. 트랜잭션 브랜치 #2를 커밋한다.

다음의 예는 위 순서에 맞게 구현된 Java 프로그램 소스 코드이다.

import java.sql.*;
import javax.sql.XAConnection;
import javax.transaction.xa.XAResource;
import com.tmax.tibero.jdbc.ext.*;

public class TwoBranchXA
{
  public static void main(String args[]) throws SQLException
  {
    createBaseTable();
    try {
          // Create XADataSource instance
          TbXADataSource xads1 = new TbXADataSource();
          xads1.setUrl("jdbc:tibero:thin:@localhost:7629:dbsvr");
          xads1.setUser("tibero");
          xads1.setPassword("tmax");
          
          TbXADataSource xads2 = new TbXADataSource();
          xads2.setUrl("jdbc:tibero:thin:@localhost:8629:dbsvr");
          xads2.setUser("wrpark");
          xads2.setPassword("tmax");
          
          // Get the XA connection
          XAConnection xacon1 = xads1.getXAConnection();
          XAConnection xacon2 = xads2.getXAConnection();

          // Get the physical connection
          Connection conn1 = xacon1.getConnection();
          Connection conn2 = xacon2.getConnection();

          // Get the XA resource
          XAResource xars1 = xacon1.getXAResource();
          XAResource xars2 = xacon2.getXAResource();   

          // Create the Xid
          Xid xid1 = createXid(1);
          Xid xid2 = createXid(2);

          // Start the resource
          xars1.start(xid1, XAResource.TMNOFLAGS);
          xars2.start(xid2, XAResource.TMNOFLAGS);

          // Execute SQL operations
          execute1(conn1);
          execute2(conn2);

          // End both the branches
          xars1.end(xid1, XAResource.TMSUCCESS);
          xars2.end(xid2, XAResource.TMSUCCESS);

          // Prepare the resource manager
          int pre1 = xars1.prepare(xid1);
          int pre2 = xars2.prepare(xid2);

          // Commit or rollback
          if (pre1 == XAResource.XA_RDONLY || pre1 == XAResource.XA_OK)
              xars1.commit(xid1, false);
          else   
              xars1.rollback(xid1);    

          if (pre2 == XAResource.XA_RDONLY || pre2 == XAResource.XA_OK)      
              xars2.commit(xid2, false);  
          else     
              xars2.rollback(xid2);   

           // Clear
           conn1.close();   
           conn1 = null; 
           conn2.close();      
           conn2 = null;    

           xacon1.close();   
           xacon1 = null;    
           xacon2.close();       
           xacon2 = null;  
        }
    catch (Exception se) {
           se.printStackTrace();    
        }     
  }
}