스프링 DB 2 정리 - 10. 스프링 트랜잭션 전파1 - 기본 (22.9.1)

0

Today I learned

목록 보기
51/75
post-thumbnail

김영한 개발자님의 스프링 DB 2편 강의를 수강하고 중요한 내용을 정리했습니다.

10. 스프링 트랜잭션 전파1 - 기본


10.1 스프링 트랜잭션 전파 - 트랜잭션 두 번 사용

트랜잭션이 각각 따로 사용되는 경우를 로그로 확인해보자.

@Test
void double_commit() {
    log.info("트랜잭션1 시작");
    TransactionStatus tx1 = txManager.getTransaction(new DefaultTransactionAttribute());
    log.info("트랜잭션1 커밋");
    txManager.commit(tx1);

    log.info("트랜잭션2 시작");
    TransactionStatus tx2 = txManager.getTransaction(new DefaultTransactionAttribute());
    log.info("트랜잭션2 커밋");
    txManager.commit(tx2);
}
트랜잭션1 시작
Creating new transaction with name [null]: 
PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Acquired Connection [HikariProxyConnection@1064414847 wrapping conn0] for JDBC transaction
Switching JDBC Connection [HikariProxyConnection@1064414847 wrapping conn0] to  manual commit

트랜잭션1 커밋
Initiating transaction commit
Committing JDBC transaction on Connection [HikariProxyConnection@1064414847 wrapping conn0]
Releasing JDBC Connection [HikariProxyConnection@1064414847 wrapping conn0]  after transaction
...2도 동일

로그를 보면 트랜잭션1과 트랜잭션2가 같은 conn0 커넥션을 사용중이다. 트랜잭션1은 conn0 커넥션을 모두 사용하고 커넥션 풀에 반납까지 완료했다. 이후에 이후에 트랜잭션2가 conn0 를 커넥션 풀에서 획득한 것이다. 따라서 둘은 완전히 다른 커넥션으로 인지해야한다.

히카리 커넥션 풀에서 커넥션을 획득하면 실제 커넥션을 그대로 반환하는 것이 아니라 내부 관리를 위해 히카리 프록시 커넥션이라는 객체를 생성해서 반환한다. 물론 내부에는 실제 커넥션이 포함되어 있다. 이 객체의 주소를 확인하면 커넥션 풀에서 획득한 커넥션을 구분할 수 있다.

결론적으로 트랜잭션을 각자 관리하기 때문에 전체 트랜잭션을 묶을 수 없다.


10.2. 스프링 트랜잭션 전파 - 전파 기본

트랜잭션을 각각 사용하는 것이 아니라, 트랜잭션이 이미 진행중인데, 여기에 추가로 트랜잭션을 수행하면 어떻게 될까?
이런 경우 어떻게 동작할지 결정하는 것을 트랜잭션 전파(propagation)라 한다.

물리 트랜잭션은 우리가 이해하는 실제 데이터베이스에 적용되는 트랜잭션을 뜻한다. 실제 커넥션을 통해서 트랜잭션을 시작(setAutoCommit(false)) 하고, 실제 커넥션을 통해서 커밋, 롤백하는 단위이다.

논리 트랜잭션은 트랜잭션 매니저를 통해 트랜잭션을 사용하는 단위이다. 논리 트랜잭션 개념은 트랜잭션이 진행되는 중에 내부에 추가로 트랜잭션을 사용하는 경우에 나타난다. 단순히 트랜잭션이 하나인 경우 둘을 구분하지는 않는다.

원칙

  • 모든 논리 트랜잭션이 커밋되어야 물리 트랜잭션이 커밋된다.
  • 하나의 논리 트랜잭션이라도 롤백되면 물리 트랜잭션은 롤백된다.

트랜잭션 참여

내부 트랜잭션이 외부 트랜잭션을 그대로 이어받아서 따른다는 뜻이다. = 외부 트랜잭션과 내부 트랜잭션이 하나의 물리 트랜잭션으로 묶이는 것이다.

외부 트랜잭션만 물리 트랜잭션을 시작하고, 커밋한다.

만약 내부 트랜잭션이 실제 물리 트랜잭션을 커밋하면 트랜잭션이 끝나버리기 때문에, 트랜잭션을 처음 시작한 외부 트랜잭션까지 이어갈 수 없다.

스프링은 이렇게 여러 트랜잭션이 함께 사용되는 경우, 처음 트랜잭션을 시작한 외부 트랜잭션이 실제 물리 트랜잭션을 관리하도록 한다. 이를 통해 트랜잭션 중복 커밋 문제를 해결한다.


10.3. 스프링 트랜잭션 전파 - 내부 롤백

내부 트랜잭션은 롤백되는데, 외부 트랜잭션이 커밋되는 상황을 알아보자.

내부 트랜잭션을 롤백하면 실제 물리 트랜잭션은 롤백하지 않는다. 대신에 트랜잭션 동기화 매니저에 rollbackOnly=true 라는 표시를 해둔다.

그 후 외부 트랜잭션이 커밋을 호출하고, 트랜잭션 동기화 매니저에 롤백 전용(rollbackOnly=true) 표시가 있는지 확인한다. 전체 트랜잭션이 롤백 전용으로 표시되어 있으면 물리 트랜잭션을 롤백한다.

스프링은 이 경우 UnexpectedRollbackException 런타임 예외를 던진다. 그래서 커밋을 시도했지만, 기대하지 않은 롤백이 발생했다는 것을 명확하게 알려준다.


10.4. 스프링 트랜잭션 전파 - REQUIRES_NEW

외부 트랜잭션과 내부 트랜잭션을 완전히 분리해서 각각 별도의 물리 트랜잭션을 사용하는 방법이다. 그래서 커밋과 롤백도 각각 별도로 이루어지게 된다.

@Test
void inner_rollback_requires_new() {
    log.info("외부 트랜잭션 시작");
    TransactionStatus outer = txManager.getTransaction(new DefaultTransactionAttribute());
    log.info("outer.isNewTransaction()={}", outer.isNewTransaction());

    log.info("내부 트랜잭션 시작");
    DefaultTransactionAttribute definition = new DefaultTransactionAttribute();

//전파 옵션인 propagationBehavior 에 PROPAGATION_REQUIRES_NEW 옵션
//내부 트랜잭션을 시작할 때 기존 트랜잭션에 참여하는 것이 아니라 
//새로운 물리 트랜잭션을 만들어서 시작하게 된다.	
    definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); 
    TransactionStatus inner = txManager.getTransaction(definition);
    log.info("inner.isNewTransaction()={}", inner.isNewTransaction());

    log.info("내부 트랜잭션 롤백");
    txManager.rollback(inner); //롤백

    log.info("외부 트랜잭션 커밋");
    txManager.commit(outer); //커밋
}

하지만 데이터베이스 커넥션이 동시에 2개 사용된다는 점을 주의해야 한다.


10.5. 스프링 트랜잭션 전파 - 다양한 전파 옵션

REQUIRED

  • 기존 트랜잭션 없음 : 새로운 트랜잭션을 생성한다.

  • 기존 트랜잭션 있음 : 기존 트랜잭션에 참여한다.

    REQUIRES_NEW

  • 기존 트랜잭션 없음 : 새로운 트랜잭션을 생성한다.

  • 기존 트랜잭션 있음 : 새로운 트랜잭션을 생성한다.

    SUPPORT

  • 기존 트랜잭션 없음: 트랜잭션 없이 진행한다.

  • 기존 트랜잭션 있음: 기존 트랜잭션에 참여한다.

    NOT_SUPPORT

  • 기존 트랜잭션 없음: 트랜잭션 없이 진행한다.

  • 기존 트랜잭션 있음: 트랜잭션 없이 진행한다. (기존 트랜잭션은 보류한다)

    MANDATORY

  • 기존 트랜잭션 없음: IllegalTransactionStateException 예외 발생

  • 기존 트랜잭션 있음: 기존 트랜잭션에 참여한다.

    NEVER

  • 기존 트랜잭션 없음: 트랜잭션 없이 진행한다.

  • 기존 트랜잭션 있음: IllegalTransactionStateException 예외 발생

    NESTED

  • 기존 트랜잭션 없음: 새로운 트랜잭션을 생성한다.

  • 기존 트랜잭션 있음: 중첩 트랜잭션을 만든다.

    중첩 트랜잭션은 외부 트랜잭션의 영향을 받지만, 중첩 트랜잭션은 외부에 영향을 주지 않는다.

  • 중첩 트랜잭션이 롤백 되어도 외부 트랜잭션은 커밋할 수 있다.

  • 외부 트랜잭션이 롤백 되면 중첩 트랜잭션도 함께 롤백된다.

profile
공부한 내용 정리

0개의 댓글