티스토리 뷰

공부흔적/스프링

Transaction 관련 내부 코드 뜯어보기

주디 𝙹𝚞𝚍𝚢 2022. 8. 2. 23:43

Transaction이 시작되고 끝나기까지 내부 코드는 과연 어떻게 돌아가는지 궁금해서 아주 조금 뜯어보았다.

2023년 7월 25일 이하 내용 추가

 본 포스팅은 @Transactional을 붙였을 때 어떻게 동작하는지 내부 코드를 뜯어본 것인데, @Transactional어노테이션을 사용할 때 프록시 기반 AOP를 사용하는데 이 AOP에 대한 내용은 빠져 있다. 트위터에 어떤 분이 AOP에 대한 내용을 풀어주셔서 살펴보고 ChatGPT가 알려준 내용을 적어둔다.


 테스트에 사용한 코드는 아래와 같다.

 5번 라인부터 보면 알 수 있듯이 JPA에 대해서는 JpaTransactionManager가 트랜잭션을 지원한다. 스프링에서는 트랜잭션 처리에 대해 PlatformTransactionManager 인터페이스를 구현하는 여러 트랜잭션 매니저를 지원하는데, JpaTransactionManager가 그중 하나이다.

 5번 라인에서 새로 만들어진 Transaction은 기본설정에 따라 전파속성을 REQUIRED로 갖는다. 이 속성은 부모 트랜잭션이 존재하면 부모 트랜잭션으로 합류하고, 그렇지 않다면 새로운 트랜잭션을 만든다. 테스트코드에서 childService.childMethod()를 호출하면서 트랜잭션 기본설정에 따라 본래의 트랜잭션에 합쳐졌다는 것을 8번 라인을 통해 알 수 있다.

 20번 라인에서 로그를 찍어주고 그 다음 라인에서 RuntimeException을 던지고, 21번 라인에서 Participating transaction failed - marking existing transaction as rollback-only로 로그가 남겨졌다. 이는 RuntimeException으로 트랜잭션을 롤백하게 되었고, 아래의 코드가 실행되었다는 의미이다.

AbstractPlatformTransactionManager 클래스 코드 일부

위 코드에서 우리가 실습하고 있는 트랜잭션이 들어가게 되는 부분은 22번째 라인이다. status.hasTransaction()을 true로 지나가게 되고, isLocalRollbackOnly와 isGlobalRollbackOnParticipationFailure 체크를 하게 되는데, isLocalRollbackOnly는 기본값이 false인 반면, isGlobalRollbackOnParticipationFailure는 기본값이 true이다. 그래서 doSetRollbackOnly를 실행하게 되고 여기에서 rollback이 마킹된다.(rollbackOnly=true)

 이렇게 childService.childMethod()에서 발생한 RuntimeException에 따라 트랜잭션의 rollbackOnly를 true로 변경해주고 다시 parentService의 childService.childMethod()를 호출했던 부분으로 돌아가게 된다. childMethod()에서 발생한 RuntimeException을 parentMethod()에서 try-catch로 잡아주기 때문에 최종커밋을 하려고 아래의 메서드를 실행하게 된다.

 위 메서드에서 23번째 라인의 status.isGlobalRollbackOnly()가 실행되면서 unexpectedRollback이 true로 변경되고, 32번째 라인에서 33번째 라인으로 들어가게 되어 최종적으로 UnexpectedRollbackException이 발생한다.

300x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함