트랜잭션 전파란, 한 트랜잭션 내에서 새로운 트랜잭션이 수행되어야 할 때 어떻게 처리할 것인가에 대한 설정이다.
이전 글에서는 외부 트랜잭션과 내부 트랜잭션이 어떻게 하나의 트랜잭션으로 수행되는지에 대해 예제 코드로 알아보았다.
이번에는 둘 중 하나라도 롤백되는 경우에 대해서 알아볼 것이다.
외부 트랜잭션의 롤백
내부 트랜잭션은 커밋되었으나, 외부 트랜잭션이 롤백되었을 때의 상황을 가정한다.
이때 논리 트랜잭션이 하나라도 롤백되었으니, 물리 트랜잭션도 롤백되어야 하므로, 내부 트랜잭션 내에서의 데이터 변경도 롤백되어야 한다.
outer_rollback()
@Test
void outer_rollback() {
log.info("외부 트랜잭션 시작");
TransactionStatus outer = txManager.getTransaction(new DefaultTransactionAttribute());
log.info("outer.isNewTransaction()={}",outer.isNewTransaction());
log.info("내부 트랜잭션 시작");
TransactionStatus inner = txManager.getTransaction(new DefaultTransactionAttribute());
log.info("inner.isNewTransaction()={}", inner.isNewTransaction());
log.info("내부 트랜잭션 커밋");
txManager.commit(inner);
log.info("외부 트랜잭션 롤백");
txManager.rollback(outer);
}
실행 결과
외부 트랜잭션 시작
Creating new transaction with name [null]:
PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Acquired Connection [HikariProxyConnection@461376017 wrapping conn0] for JDBC
transaction
Switching JDBC Connection [HikariProxyConnection@461376017 wrapping conn0] to
manual commit
내부 트랜잭션 시작
Participating in existing transaction
내부 트랜잭션 커밋
외부 트랜잭션 롤백
Initiating transaction rollback
Rolling back JDBC transaction on Connection [HikariProxyConnection@461376017
wrapping conn0]
Releasing JDBC Connection [HikariProxyConnection@461376017 wrapping conn0]
after transaction
- 외부 트랜잭션이 시작되고, 내부 트랜잭션이 시작된 후 외부 트랜잭션에 참여되었음을 알 수 있다.
- 내부 트랜잭션은 커밋되었으나, 참여된 트랜잭션이므로 물리 트랜잭션의 커밋은 외부 트랜잭션이 커밋될 때 발생하므로 로그가 뜨지 않았다.
- Initiation transaction rollback: 외부 트랜잭션이 롤백되어 물리 트랜잭션 전체가 롤백되었다.
따라서 DB에 반영되는 물리 트랜잭션이 롤백되었으므로, 내부 트랜잭션의 수행 결과는 DB에 반영되지 않았다.
내부 트랜잭션의 롤백
이번에는 내부 트랜잭션이 롤백되었고, 외부 트랜잭션이 커밋되었을때의 상황이다.
inner_rollback()
@Test
void inner_rollback() {
log.info("외부 트랜잭션 시작");
TransactionStatus outer = txManager.getTransaction(new DefaultTransactionAttribute());
log.info("outer.isNewTransaction()={}",outer.isNewTransaction());
log.info("내부 트랜잭션 롤백");
TransactionStatus inner = txManager.getTransaction(new DefaultTransactionAttribute());
log.info("inner.isNewTransaction()={}", inner.isNewTransaction());
log.info("내부 트랜잭션 롤백");
txManager.rollback(inner);
log.info("외부 트랜잭션 커밋");
txManager.commit(outer);
}
실행 결과
외부 트랜잭션 시작
Creating new transaction with name [null]:
PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Acquired Connection [HikariProxyConnection@220038608 wrapping conn0] for JDBC
transaction
Switching JDBC Connection [HikariProxyConnection@220038608 wrapping conn0] to
manual commit
내부 트랜잭션 시작
Participating in existing transaction
내부 트랜잭션 롤백
Participating transaction failed - marking existing transaction as rollback-only
Setting JDBC transaction [HikariProxyConnection@220038608 wrapping conn0]
rollback-only
외부 트랜잭션 커밋
Global transaction is marked as rollback-only but transactional code requested
commit
Initiating transaction rollback
Rolling back JDBC transaction on Connection [HikariProxyConnection@220038608
wrapping conn0]
Releasing JDBC Connection [HikariProxyConnection@220038608 wrapping conn0]
after transaction
- 내부 트랜잭션이 시작되어 외부 트랜잭션에 참여한 과정까지는 이전과 동일하다.
- Participating transaction failed: 참여한 내부 트랜잭션이 실패했다.
- Setting JDBC transaction...: JDBC 트랜잭션에 rollback-only로 세팅되었음을 보여준다.
- 외부 트랜잭션은 커밋을 요구했으나, 글로벌 트랜잭션이 롤백 온리로 표시되어졌다.
- 최종적으로 물리 트랜잭션의 롤백이 실행되었다.
UnexpectedRollbackException
내부 트랜잭션 롤백 전파 문제는 중요하게 다루어야한다.
- 시스템 입장에서 커밋을 호출했지만 롤백이 되었다는 것을 분명하게 알려주어야 한다.
- 고객이 상품을 주문할 때, 주문을 성공했다고 생각했는데 내부 트랜잭션에서 롤백이 되어 실제 주문이 생성되지 않은 경우가 발생할 수 있다.
스프링은 이 경우 UnexpectedRollbackException 런타임 예외를 던져 예상치 못한 롤백이 발생했음을 명확하게 알려준다.
출처
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-2/dashboard
스프링 DB 2편 - 데이터 접근 활용 기술 - 인프런 | 강의
백엔드 개발에 필요한 DB 데이터 접근 기술을 활용하고, 완성할 수 있습니다. 스프링 DB 접근 기술의 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., - 강의 소개 | 인
www.inflearn.com
'🍃Spring' 카테고리의 다른 글
[Spring] MapStruct 사용시 매핑이 제대로 안될 경우 (0) | 2023.02.14 |
---|---|
[Spring] 트랜잭션 전파 - REQUIRES_NEW 활용하기 (0) | 2023.02.05 |
[Spring] 트랜잭션 전파 - 1 (0) | 2023.02.05 |
[Spring] 트랜잭션 - 프록시 내부 호출 문제 2 (0) | 2023.02.03 |
[Spring] 트랜잭션 - 프록시 내부 호출 문제 1 (0) | 2023.02.03 |