이번엔 외부 트랜잭션이 롤백되는 경우를 보자.
@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 |