스프링/6. 스프링 DB-2

51. 트랜잭션 전파 4

sdafdq 2023. 10. 16. 18:25

이번엔 외부 트랜잭션이 롤백되는 경우를 보자.

 

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

    log.info("내부 트랜잭션 시작");
    TransactionStatus status2 = txManager.getTransaction(new DefaultTransactionAttribute());
    log.info("내부 트랜잭션 .isNewTransaction()={}", status2.isNewTransaction());

    log.info("내부 트랜잭션 commit");
    txManager.commit(status2);

    log.info("외부트랜잭션 rollback");
    txManager.rollback(status1);
}

결과는 보나마나 전체가 롤백이 된다.

외부 트랜잭션에다가 rollback은 하나라도 rollback이면 rollback되니.

내부 트랜잭션은 위와 같이 커밋 등의 언급을 안해도(내부 트랜잭션은 커밋, 롤백 굳이 안해놔도 외부트랜잭션 따라가긴 함. ) 외부트랜잭션 롤백만 해도 롤백이 된다.

 

내부 트랜잭션은 commit되면 뭐랄까 좀 트랜잭션 내부적으로 세이브포인트? 저장시켜놓고 커넥션으로 뭘 하거나 그런 건 없다.

 

 

 

그럼 반대로, 내부 트랜잭션에서 rollback되고 외부 트랜잭션에서 commit 되는 경우

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

    log.info("내부 트랜잭션 시작");
    TransactionStatus status2 = txManager.getTransaction(new DefaultTransactionAttribute());
    log.info("내부 트랜잭션 .isNewTransaction()={}", status2.isNewTransaction());

    log.info("내부 트랜잭션 rollback");
    txManager.rollback(status2);

    log.info("외부트랜잭션 commit");
    txManager.commit(status1);
}

뭐 두번째 트랜잭션 얻는 것 까지는 똑같다.

 

근데 그 후, 내부 트랜잭션(두번째 트랜잭션)을 먼저 롤백한다.

그럼 어떤 로그가 찍히냐면,

Participating transaction failed - marking existing transaction as rollback-only

트랜잭션 참여 실패 - 마킹합니다 기존의 트랜잭션을 롤백 온리로.

 

rollback 하나만 나오면 모든 트랜잭션이 rollback 되지만, 그렇다고 그 rollback나온 트랜잭션에서 물리 트랜잭션을 가져다 쓰는 게 아니다. 여전히 물리 트랜잭션은 외부 트랜잭션, 첫번째 트랜잭션만 관리를 한다.

 

다른 것들은 아마

저 트랜잭션 객체가 아마 isRollbackOnly 뭐 이런 식으로 bool 필드를 가지고 있고, 저기다 true 값 넣어준 다던지 그렇게 하는 듯 싶다.

 

그리고 이제 외부 트랜잭션이 commit 하는 순간

Global transaction is marked as rollback-only but transactional code requested commit

이렇게 글로벌 트랜잭션이 롤백 온리로 마크되어 있는데, 트랜잭션 코드는 커밋을 요구한다고 로그가 찍힌다.

 

그리고 그 후, 롤백이 된다.

 

내부 트랜잭션은 물리 트랜잭션을 직접 호출하여 롤백시키는 것이 아니라, 아마 트랜잭션 커넥션 객체? 트랜잭션 동기화 매니저 객체?를 롤백 온리로 만들어 버린다.

 

 

근데 좀 짜증났던게,

저게 저렇게 내부 트랜잭션에 의해 롤백 온리로 바뀌게 된 상태에서 외부 트랜잭션이 commit을 시도하면,

예외가 터진다. UnexpectedRollbackException 예외

처음에 '아니 그냥 롤백 시켜버리면 끝이지 왜 예외를 발생시키지?'

이랬는데, 생각해보니 확실히 정상적인 상황은 아니고, 그걸 클라이언트에게 알릴 필요가 있을 수 있다.

 

아니 또 근데 외부 트랜잭션의 롤백에 의한 롤백은 또 예외를 뱉거나 그러진 않는다.. 

 

또 저게, 런타임 예외일 수도 있고, 비즈니스 예외, 체크예외일 수도 있다..

 

근데 우리가 rollback명령하는 란에 rollback 명령 후 throw 해서 예외를 날릴 수도 있으니까..

 

아마 외부 트랜잭션에서 rollback을 하면 비록 코드는 정상적으로 돌아가지만 우리가 예외를 날리도록 하지 않을까? 싶다.

'스프링 > 6. 스프링 DB-2' 카테고리의 다른 글

53. 트랜잭션 전파 6  (0) 2023.10.16
52. 트랜잭션 전파 5  (0) 2023.10.16
50. 트랜잭션 전파 3  (0) 2023.10.16
49. 스프링 트랜잭션 전파 2  (0) 2023.10.16
48. 스프링 트랜잭션 전파  (0) 2023.10.16