스프링 DB 2편(데이터 접근 활용 기술)

Ch11. 스프링 트랜잭션 전파(활용) - 복구 REQUIRES_NEW

webmaster 2022. 7. 8. 19:08
728x90

회원 가입을 시도한 로그를 남기는데 실패하더라도 회원 가입은 유지되어야 한다.

이 요구사항을 만족하기 위해서 로그와 관련된 물리 트랜잭션을 별도로 분리해보자. 바로 REQUIRES_NEW를 사용하는 것이다

/**
 * memberService    @Transactional:ON
 * memberRepository @Transactional:ON
 * logRepository    @Transactional:ON(REQUIRED_NEW) Exception
 */
@Test
public void recoverException_success() {
    //given
    String username = "로그예외_recoverException_success";

    //when
    memberService.joinV2(username);
    
    //then : member 저장, log 롤백
    assertTrue(memberRepository.find(username).isPresent());
    assertTrue(logRepository.find(username).isEmpty());
}
  • MemberService = @Transactional:ON
  • MemberRepository = @Transactional:ON
  • LogRepository @Transactional(REQUIRES_NEW)

LogRepository - save()

@Transactional(propagation = REQUIRES_NEW)
public void save(Log logMessage){
    log.info("log 저장");
    em.persist(logMessage);

    if(logMessage.getMessage().contains("로그예외")){
        log.info("log 저장시 예외 발생");
        throw new RuntimeException("예외 발생");
    }
}
  • 기존 트랜잭션에 참여하는 REQUIRED 대신에, 항상 신규 트랜잭션을 생성하는 REQUIRES_NEW 를 적용하자

REQUIRES_NEW - 물리 트랜잭션 분리

물리 트랜잭션 분리

  • MemberRepository 는 REQUIRED 옵션을 사용한다. 따라서 기존 트랜잭션에 참여한다.
  • LogRepository의 트랜잭션 옵션에 REQUIRES_NEW를 사용했다.
    • REQUIRES_NEW는 항상 새로운 트랜잭션을 만든다. 따라서 해당 트랜잭션 안에서는 DB 커넥션도 별도로 사용하게 된다

REQUIRES_NEW - 복구

복구

  • REQUIRES_NEW를 사용하게 되면 물리 트랜잭션 자체가 완전히 분리되어 버린다.
  • 그리고 REQUIRES_NEW 는 신규 트랜잭션이므로 rollbackOnly 표시가 되지 않는다. 그냥 해당 트랜잭션이 물리 롤백되고 끝난다

Required_New 동작 과정 상세

  • LogRepository에서 예외가 발생한다. 예외를 던지면 LogRepository의 트랜잭션 AOP가 해당 예외를 받는다.
  • REQUIRES_NEW 를 사용한 신규 트랜잭션이므로 물리 트랜잭션을 롤백한다. 물리 트랜잭션을 롤백했으므로 rollbackOnly를 표시하지 않는다. 여기서 REQUIRES_NEW를 사용한 물리 트랜잭션은 롤백되고 완전히 끝이 나버린다.
  • 이후 트랜잭션 AOP는 전달 받은 예외를 밖으로 던진다.
  • 예외가 MemberService 에 던져지고, MemberService는 해당 예외를 복구한다. 그리고 정상적으로 리턴한다.
  • 정상 흐름이 되었으므로 MemberService 의 트랜잭션 AOP는 커밋을 호출한다.
  • 커밋을 호출할 때 신규 트랜잭션이므로 실제 물리 트랜잭션을 커밋해야 한다. 이때 rollbackOnly를 체크한다.
  • rollbackOnly 가 없으므로 물리 트랜잭션을 커밋한다.
  • 이후 정상 흐름이 반환된다.

주의

  • REQUIRES_NEW 를 사용하면 하나의 HTTP 요청에 동시에 2개의 데이터베이스 커넥션을 사용하게 된다. 따라서 성능이 중요한 곳에서는 이런 부분을 주의해서 사용해야 한다.
  • REQUIRES_NEW를 사용하지 않고 문제를 해결할 수 있는 단순한 방법이 있다면, 그 방법을 선택하는 것이 더 좋다

EX) 아래와 같은 구조로 변경한다.

구조 변경

  • REQUIRES_NEW를 사용하지 않고 구조를 변경하여 Connection을 1개만 사용한다.
    • 이렇게 하면 HTTP 요청에 동시에 2개의 커넥션을 사용하지는 않는다. 순차적으로 사용하고 반환하게 된다
  • 물론 구조상 REQUIRES_NEW 를 사용하는 것이 더 깔끔한 경우도 있으므로 각각의 장단점을 이해하고 적절하게 선택해서 사용하면 된다
728x90