Josh 성장일기
article thumbnail
Published 2024. 8. 13. 13:28
트랜잭션 전파 REQUIRES_NEW Spring

안녕하세요 오늘은 개발공부를 같이하는 친구와 공부도중 새로운 트랜잭션 전파속성중 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 로 새로운 트랜잭션이 할당된것을 위의 로그로 확인해볼수 있음)

 

아무것도 insert 되지 않음

 

 

혼란에 빠져있는 사이 블로그 글을 하나 보게됩니다!! 

https://techblog.woowahan.com/2606/ 

 

응? 이게 왜 롤백되는거지? | 우아한형제들 기술블로그

이 글은 얼마 전 에러로그 하나에 대한 호기심과 의문으로 시작해서 스프링의 트랜잭션 내에서 예외가 어떻게 처리되는지를 이해하기 위해 삽질을 해본 경험을 토대로 쓰여졌습니다. 스프링의

techblog.woowahan.com

바로 개발자들에게 빛과 소금과도 같은 빅테크기업의 기술블로그(위의 글을 정독!!)

 

위의 글에 대하여 요약하자면 @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 메서드는 잘 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 의 원리에 대하여 개념을 다시 잡을수 있어서 매우 좋은 공부였다고 생각합니다.

profile

Josh 성장일기

@JoshDev

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그