안녕하세요 오늘은 개발공부를 같이하는 친구와 공부도중 새로운 트랜잭션 전파속성중 REQUIRES_NEW에 대해서
새롭게 알게된 사실을 기록하려고 합니다!!
환경
언어: kotlin
JDK 버전: JDK 21
DB: Maria DB
프레임워크: Spring Boot 3.3.2
이 글을 작성하기전에 저는 전파속성에서 REQUIRES_NEW 에 대하여 새로운 트랜잭션을 할당하게되면서 새로운 트랜잭션안에서의 Exception 은 부모 트랜잭션 즉 선행트랜잭션에 대해 영향을 끼치치 않아 선행트랜잭션의 Save메서드는 Commit이 되고 REQUIRES_NEW로 할당된 새로운 트랜잭션만 Rollback 이 된다. 라는 개념으로 이해하고 있었습니다.
하지만 제가 알고있던 개념은 잘못된 이해였다는것을 알게되었습니다.
@Service
class ParentService(
private val childService: ChildService,
private val testEntityRepository: TestEntityRepository
) {
@Transactional
fun parentServiceMethod() {
testEntityRepository.save(TestEntity(null,"parentService call save method"))
childService.childServiceMethod()
}
}
@Service
class ChildService(
private val testEntityRepository: TestEntityRepository
) {
@Transactional(propagation = Propagation.REQUIRES_NEW)
fun childServiceMethod() {
testEntityRepository.save(TestEntity(null, "childService call save method"))
throw RuntimeException()
}
}
위의 코드에서처럼 parentServiceMethod 에서 먼저 Jpa save 메서드를 호출한 이후 전파속성을 REQUIRES_NEW로 설정하여 새롭게 할당받은 트랜잭션인 childServiceMethod 를 호출하게 되면서 childServiceMethod 는 Jpa save 메서드를 진행한 후 Unchecked Exception 인 RuntimeException을 던지게 됩니다.
위의 코드처럼 작성하게 되면 저는 childServiceMethod 의 save 메소드에 대한 insert 는 Rollback 이 진행되고
parentServiceMethod 에서 save 메소드에 대한 insert 는 commit이 되는것을 예상했습니다.
하지만 제 예상대로 로직이 진행되지않고 부모메서드, 자식메서드에서 호출된 insert 모두 Rollback 이 되는것을 알 수 있었습니다. (REQUIRES_NEW 로 새로운 트랜잭션이 할당된것을 위의 로그로 확인해볼수 있음)
혼란에 빠져있는 사이 블로그 글을 하나 보게됩니다!!
https://techblog.woowahan.com/2606/
바로 개발자들에게 빛과 소금과도 같은 빅테크기업의 기술블로그(위의 글을 정독!!)
위의 글에 대하여 요약하자면 @Tx 에서 Exception 이 터지면 롤백 마크를 하게되는데 try ~ catch 를 하여도 한번 Rollback 마크가 남은 트랜잭션은 재사용이 불가능하다는거였습니다.
저는 지금 REQUIRES_NEW 를 통해 새로운 트랜잭션을 할당받아 사용하고 있어서 '부모메서드에서 자식메서드에 대한 try ~ catch 를 진행하게 된다면 자식메서드 트랜잭션은 Rollback 마크가 남아 롤백이 되지만 부모메서드의 트랜잭션에서는 Rollback 마크가 남지 않겠구나' 라고 생각하여 다시 한번 실습을 해보았습니다!!
@Transactional
fun parentServiceMethod() {
testEntityRepository.save(TestEntity(null,"parentService call save method"))
try {
childService.childServiceMethod()
} catch (e: RuntimeException) {
logger.info("부모메서드에서 자식메소드를 catch")
}
}
위처럼 부모메서드에서 자식메소드가 던진 Exception 을 Catch하는 로직으로 변경하고 실행해보니
위처럼 부모메서드에서 호출한 save 메서드는 잘 Commit 이 되고 자식메서드의 save 메서드는 Rollback 이 된것을 확인할 수 있었습니다.
정리
1. @Transactional 이 붙은 메서드에서 Exception 이 발생하면 해당 트랜잭션은 롤백마크가 생성되며 해당 트랜잭션은 재사용이 불가능
2. 부모 트랜잭션에서 save 메서드를 호출후 자식메서드에서 REQUIRED 를 통해 전파받은 트랜잭션에서 Exception 이 터진다면 롤백마크가 남게되어 부모메서드에서 try ~ catch 를 해주어도 같은 트랜잭션이기 때문에 모두다 Rollback
3. 부모 트랜잭션에서 save 메서드를 호출후 자식메서드에서 REQUIRES_NEW 를 통해 새롭게 할당한 트랜잭션에서
Exception 이 터진다해도 부모메서드에서 try ~ catch 를 해주지 않으면 Exception 이 전달되어 부모트랜잭션도 롤백마크가 생겨 Rollback
4. 2번과 동일한 환경 :: 부모메서드에서 try ~ catch 를 해주게 된다면 롤백마크가 남은 자식트랜잭션만 Rollback,
롤백마크가 남지않은 부모트랜잭션은 Commit
REQUIRES_NEW 전파속성과 트랜잭션 Rollback 및 Commit 의 원리에 대하여 개념을 다시 잡을수 있어서 매우 좋은 공부였다고 생각합니다.
'Spring' 카테고리의 다른 글
[트러블슈팅 기억 기록용] Querydsl(영속성 컨텍스트) (0) | 2024.02.16 |
---|---|
Spring Security + jwt 사용기(우당탕탕 주의) - 3 (0) | 2023.05.02 |
Spring Security + jwt 사용기(우당탕탕 주의) - 2 (0) | 2023.04.04 |
Spring Security + jwt 사용기(우당탕탕 주의) - 1 (0) | 2023.03.31 |
Spring Filter에 대하여 (0) | 2023.03.15 |