ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • spring transaction 전파
    카테고리 없음 2023. 4. 21. 15:26
    이번 시간에는 spring transaction 전파에 대해서 알아볼 예정이다. transaction 전파란 현재 transaction 에서 다른 transaction 으로 이동할 때를 이야기 한다. 예를들어 AccountService에 transaction이 걸려 있는데 다른 OrderService 에서도 transaction 이 걸려 있는 것을 말한다. 같은 클래스는 해당 사항이 없다. 간단하게 확인 가능한 테스트 코드를 보자. 아래는 AccountService클래스 이다.
    @Autowired
    private TransactionService transactionService;
    
    @Transactional
    public AccountTest transactionTest(AccountTest accountTest) {
      log.info("currentTransactionName : {}", TransactionSynchronizationManager.getCurrentTransactionName());
      transactionService.transactionService();
      return accountRepository.save(accountTest);
    }
    
    이번에는 TransactionService이다.
    public void transactionService(){
      log.info("currentTransactionName : {}" , TransactionSynchronizationManager.getCurrentTransactionName());
    }
    
    그냥 현재 트랜잭션 명을 출력해주는 그런 코드이다. 일단 기본적으로 위와 테스트를 돌려보자.
    @Test
    public void transactionTest(){
      AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(RootConfiguration.class);
      AccountService bean = annotationConfigApplicationContext.getBean(AccountService.class);
      AccountTest accountTest = new AccountTest();
      accountTest.setName("wonwoo");
      AccountTest save = bean.transactionTest(accountTest);
      System.out.println(save);
    }
    
    로그를 확인해보면 다음과 같다.
    21:41:55.043 [main] INFO  me.wonwoo.service.AccountService - currentTransactionName : me.wonwoo.service.AccountService.transactionTest
    21:41:55.043 [main] INFO  me.wonwoo.service.TransactionService - currentTransactionName : me.wonwoo.service.AccountService.transactionTest
    
    똑같은 트랜잭션 명이다. 그럼 이번에는 transactionService 메서드에 @Transactional을 달아보자.
    @Transactional
    public void transactionService() {
      log.info("currentTransactionName : {}", TransactionSynchronizationManager.getCurrentTransactionName());
    }
    
    그리고 나서 확인해보면 아래와 같은 로그가 출력 될 것이다.
    21:43:29.874 [main] INFO  me.wonwoo.service.AccountService - currentTransactionName : me.wonwoo.service.AccountService.transactionTest
    21:43:29.896 [main] INFO  me.wonwoo.service.TransactionService - currentTransactionName : me.wonwoo.service.AccountService.transactionTest
    
    위에 것과 동일하게 동일한 명으로 출력된다. 기본적으로 Spring의 트랜잭션은 전파되는 것을 알수 있다. @Transactional속성 중 Propagation를 보면 전파 속성을 지정할 수 있다. 기본값은 Propagation.REQUIRED이다. 이번에는 다른 속성을 넣어보자.
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void transactionService() {
      log.info("currentTransactionName : {}", TransactionSynchronizationManager.getCurrentTransactionName());
    }
    
    위와 같이 해서 테스트를 돌려본다면 아래와 같은 로그를 확인 할 수 있다.
    21:46:41.782 [main] INFO  me.wonwoo.service.AccountService - currentTransactionName : me.wonwoo.service.AccountService.transactionTest
    21:46:41.802 [main] INFO  me.wonwoo.service.TransactionService - currentTransactionName : me.wonwoo.service.TransactionService.transactionService
    
    트랜잭션 명이 다르다. 위와 속성은 새로 트랜잭션을 만드는 것이다. 만약 기존에 트랜잭션이 있더라도 현재 트랜잭션을 새로 만드는 속성이다.
    @Transactional(propagation = Propagation.NEVER)
    public void transactionService() {
      log.info("currentTransactionName : {}", TransactionSynchronizationManager.getCurrentTransactionName());
    }
    
    다음은 Propagation.NEVER 속성이다. 이것을 테스트를 해보면 에러가 발생한다. 아래와 같은 에러가 발생한다.
    org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation never
    
    트랜잭션이 이미 있으면 에러를 발생한다. 만약 트랜잭션이 시작된것이 없다면 기존과 동일하게 트랜잭션을 시작한다. 다음으로는 Propagation.MANDATORY 속성에 대해 보자.
    @Transactional(propagation = Propagation.MANDATORY)
    public void transactionService() {
      log.info("currentTransactionName : {}", TransactionSynchronizationManager.getCurrentTransactionName());
    }
    
    이것을 테스트를 해보면 트랜잭션이 전파 된다. 로그는 아래와 같다.
    21:53:03.336 [main] INFO  me.wonwoo.service.AccountService - currentTransactionName : me.wonwoo.service.AccountService.transactionTest
    21:53:03.350 [main] INFO  me.wonwoo.service.TransactionService - currentTransactionName : me.wonwoo.service.AccountService.transactionTest
    
    하지만 만약 트랜잭션이 없는 곳에서 호출 된다면 다음과 같은 에러가 발생된다.
    org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation mandatory
    
    이번에는 NOT_SUPPORTED 속성이다.
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void transactionService() {
      log.info("currentTransactionName : {}", TransactionSynchronizationManager.getCurrentTransactionName());
      AccountTest accountTest = new AccountTest();
      accountTest.setName("wonwoo123");
      accountRepository.save(accountTest);
    
    }
    
    NOT_SUPPORTED 새로운 트랜잭션이 생성된다. 새로운 트랜잭션이라면 Propagation.REQUIRES_NEW 와 동일한 기능이 아닌가? 하지만 약간 다르다. Propagation.REQUIRES_NEW는 새롭긴 하지만 부모(새로만들어지기전) 트랜잭션의 영향이 있다. 부모 트랜잭션이 에러가 발생하면 새로 만들어진 트랜잭션도 롤백이 된다.한마디로 기존 트랜잭션을 잠시 보류하고 새로운 트랜잭션을 진행한 후에 다시 기존 트랜잭션이 동작하게 된다. NOT_SUPPORTED 경우에는 별개다 만약 부모 트랜잭션에서 오류가 발생하여 롤백을 해도 새로운 트랜잭션은 롤백되지 않는다.
    @Transactional
    public AccountTest transactionTest(AccountTest accountTest) {
      log.info("currentTransactionName : {}", TransactionSynchronizationManager.getCurrentTransactionName());
      transactionService.transactionService();
      AccountTest save = accountRepository.save(accountTest);
      if (true) {
        throw new RuntimeException();
      }
    
      return save;
    }
    
    이와 같이 강제로 에러를 발생하게 했다. 이외에도 몇가지가 더 2가지 정도다 더 있다. 하지만 필지가 정확하게 이해 한 것이 아니기에 나머지는 테스트 해보지 않았다. 이상으로 트랜잭션 전파의 테스트를 진행 해봤다.

    댓글

Designed by Tistory.