이번에는 트랜잭션이 중첩되어있지만 각각의 내 외부 트랜잭션을 완전히 분리해 **각각 다른 커넥션(물리 트랜잭션)**을 사용하도록 하는 방법을 알아보겠다.

스프링 트랜잭션 전파7 - REQUIRES_NEW

학습 페이지

이 방법은 내부트랜잭션에 문제가 생겨 롤백해도 외부트랜잭션에 영향을 주지 않는다.

image.png

다시 말하지만 별도의 물리트랜잭션을 가진다는건 별도의 DB 커넥션을 가진다는 의미이다.

   @Test
    void inner_rollback_requires_new(){
        log.info("외부 트랜잭션 시작");
        TransactionStatus outer = txManager.getTransaction(new DefaultTransactionAttribute());
        //외부 트랜잭션인지 확인
        log.info("outer.isNewTransaction()={}", outer.isNewTransaction());

        log.info("내부 트랜잭션 시작");
        //내부 트랜잭션에 PROPAGATION_REQUIRES_NEW 설정
        DefaultTransactionAttribute definition = new DefaultTransactionAttribute();
        definition.setPropagationBehaviorName("PROPAGATION_REQUIRES_NEW");
        TransactionStatus inner = txManager.getTransaction(definition);
        //내부 트랜잭션인지 확인
        log.info("inner.isNewTransaction()={}", inner.isNewTransaction());

        log.info("내부 트랜잭션 롤백");
        txManager.rollback(inner);

        log.info("외부 트랜잭션 커밋");
        txManager.commit(outer);
    }

내부 트랜잭션을 트랜잭션 매니저에서 가져올때 PROPAGATION_REQUIRES_NEW 라는 어트리뷰트를 갖게끔 설정하면 이제 내부트랜잭션이 별도의 커넥션을 통해 이뤄지게 된다.

image.png

로그를 순서대로 보자. 먼저 outer를 위한 새 트랜잭션이 만들어진다. 그리고 이전 외부 트랜잭션을 잠시 대기(suspending)시켜놓고, inner를 위한 새 트랜잭션을 만든다.

이후 inner를 롤백시키고, 다시 대기했던 트랜잭션을 재개(Resuming)한다. 재개된 트랜잭션을 커밋한다.

image.png

대기(suspending)과 재개(resuming)은 트랜잭션 동기화 매니저 수준에서 이해하면 편하다. 현재 쓰레드에서 진행중인 트랜잭션이 기존에 conn1이었는데, 동기화 매니저가 다른 커넥션(conn2)을 사용하려면 기존의 conn1을 대기시키는 것. 그리고 conn2가 사용이 끝나고 반환되면 다시 conn1 사용을 재개(resuming)한다.

image.png

⚠️ 주의!

이 방식은 요청은 하난데 커넥션이 여러개 사용된다.

따라서 트래픽이 많거나 성능이 중요한 상황에서는 주의해서 사용해야 하는 옵션이다.