Closed binchoo closed 3 years ago
연관 이슈: https://github.com/caffeine-library/pro-spring-5/issues/68
(1) 트랜잭션 전파 유형 (2) 예외 발생 위치: 부모 / 자식 트랜잭셔널 메서드
위 두 조건에 따라 롤백 동작이 어디까지 수행되는지 확인하는 것입니다.
비즈니스 요구사항 하나는, 하위에서 여러 서비스의 협업으로 구현되므로 이 요구사항이 온전히 달성되려면 각 서비스의 장애에 대한 대응이 필요합니다.
허나, 하위 서비스가 비즈니스 달성에 밀접한 연관을 갖지 않을 수도, 다른 장애에 면역을 갖고 모든 이력을 남겨야 할 수도 있습니다.
이렇듯 장애 처리(롤백)의 범위를 조정하는 개념이 트랜잭셔널 메서드의 '트랜잭션 전파'에 담겨 있는 것으로 생각됩니다.
PROPAGATION_REQUIRED: 이미 존재하는 트랜잭션을 지원합니다. 트랜잭션이 없으면 새 트랜잭션이 시작됩니다.
하지만 '트랜잭션 전파'의 스펙은 기술적인 수준에서 딱딱하게 설명되어 그 개념이 등장한 상위 배경이 파악되지가 않습니다.
따라서 테스트 코드를 작성하여 전파 유형에 따른 롤백 범위가 어떻게 되는지 살펴보려 합니다.
두 개의 boolean 칼럼 OuterCommit, InnerCommit를 갖습니다.
OuterCommit
InnerCommit
OuterService와 InnerService가 있습니다.
OuterService
InnerService
public interface OuterService { void setInnerService(InnerService innerService); void updateColumn(Long id, boolean outerException, ExceptionLocation innerExceptionLocation); } public interface InnerService { void updateColumn(Long id, ExceptionLocation exLocation); }
엔터티의 OuterCommit과 InnerCommit을 TRUE로 갱신하는 비즈니스 로직을 갖춥니다.
OuterService는 예외 발생 지점을 설정할 수 있습니다.
OuterService는 트랜잭션 하에 수행됩니다. (전파 정책: PROPAGATION_REQUIRED)
OuterService는 InnerService가 뱉은 예외를 Consume해야 합니다.
@Service public class OuterServiceImpl implements OuterService { @Autowired private SimpleDataRepository repository; private InnerService innerService = null; @Override public void setInnerService(InnerService innerService) { this.innerService = innerService; } @Transactional(propagation = Propagation.REQUIRED) @Override public void updateColumn(Long id, boolean outerException, ExceptionLocation innerExceptionLocation) { try { innerService.updateColumn(id, innerExceptionLocation); } catch (RuntimeException e) { e.printStackTrace(); } updateOuterColumn(id); if (outerException) { throw new RuntimeException(); } } private void updateOuterColumn(Long id) { SimpleData data = repository.findById(id).orElseGet(()->{ throw new IllegalArgumentException(); }); data.setOuterCommit(true); repository.save(data); } }
InnerService는 엔터티의 InnerCommit을 TRUE로 갱신하는 로직을 수행합니다.
InnerService는 InnerCommit 갱신 직전 또는 직후에 에러를 발생시킬 수 있습니다.
InnerService는 트랜잭션 전파 정책이 변경되며 테스트됩니다.
자식 트랜잭셔널 메서드의 전파 요구사항을 변경하며 테스트하기 위해
전파 정책별로 InnerService 인터페이스의 하위 구현체를 생성합니다. 그리고 @Transactional로 전파 요구사항을 기입합니다.
@Transactional
ex)
@Transactional(propagation = Propagation.MANDATORY) @Service("innerServiceMandatory") public class InnerServiceMandatory implements InnerService { @Autowired private InnerServiceImpl impl; @Override public void updateColumn(Long id, ExceptionLocation eLocation) { impl.updateColumn(id, eLocation); } }
칼럼 갱신은 데이터에 직접 접근하는 또 다른 InnserSerivce 구현체에 위임합니다.
InnserSerivce
@Service public class InnerServiceImpl implements InnerService { @Autowired private SimpleDataRepository repository; @Override public void updateColumn(Long id, ExceptionLocation exLocation) { SimpleData data = repository.findById(id).orElseGet(()->{ throw new IllegalArgumentException(); }); data.setInnerCommit(true); if (ExceptionLocation.BEFORE_UPDATE == exLocation) exLocation.throwException(); repository.save(data); if (ExceptionLocation.AFTER_UPDATE == exLocation) exLocation.throwException(); } }
https://github.com/caffeine-library/pro-spring-5/blob/main/references/code/binchoo/TransactionPropagationTestProject/src/test/java/org/binchoo/env/propagation/test/PropagationCombinationsTest.java
칼럼 값이 TRUE이면, 해당 칼럼은 갱신된 것으로 봅니다. 칼럼 값이 FALSE이면, 아예 갱신 로직에 접근하지 못 했거나 롤백된 것입니다. assert~Rollback() 메서드는 두 용도로 활용되니 유의하여야 합니다.
assert~Rollback()
private void assertOuterCommit() { assertThat(repository.findById(1L).get().getOuterCommit()).isTrue(); } private void assertInnerCommit() { assertThat(repository.findById(1L).get().getInnerCommit()).isTrue(); } private void assertOuterRollback() { assertThat(repository.findById(1L).get().getOuterCommit()).isFalse(); } private void assertInnerRollback() { assertThat(repository.findById(1L).get().getInnerCommit()).isFalse(); }
https://github.com/caffeine-library/pro-spring-5/issues/68#issuecomment-948208664 해당 테스트 수행하여 정리해 본 Propagation 정책의 의미입니다.
@caffeine-library/readers-pro-spring-5
연관 이슈: https://github.com/caffeine-library/pro-spring-5/issues/68
목적
(1) 트랜잭션 전파 유형 (2) 예외 발생 위치: 부모 / 자식 트랜잭셔널 메서드
위 두 조건에 따라 롤백 동작이 어디까지 수행되는지 확인하는 것입니다.
개요
비즈니스 요구사항 하나는, 하위에서 여러 서비스의 협업으로 구현되므로 이 요구사항이 온전히 달성되려면 각 서비스의 장애에 대한 대응이 필요합니다.
허나, 하위 서비스가 비즈니스 달성에 밀접한 연관을 갖지 않을 수도, 다른 장애에 면역을 갖고 모든 이력을 남겨야 할 수도 있습니다.
이렇듯 장애 처리(롤백)의 범위를 조정하는 개념이 트랜잭셔널 메서드의 '트랜잭션 전파'에 담겨 있는 것으로 생각됩니다.
하지만 '트랜잭션 전파'의 스펙은 기술적인 수준에서 딱딱하게 설명되어 그 개념이 등장한 상위 배경이 파악되지가 않습니다.
따라서 테스트 코드를 작성하여 전파 유형에 따른 롤백 범위가 어떻게 되는지 살펴보려 합니다.
엔터티: SimpleData
두 개의 boolean 칼럼
OuterCommit
,InnerCommit
를 갖습니다.서비스
OuterService
와InnerService
가 있습니다.OuterService
엔터티의 OuterCommit과 InnerCommit을 TRUE로 갱신하는 비즈니스 로직을 갖춥니다.
OuterService는 예외 발생 지점을 설정할 수 있습니다.
OuterService는 트랜잭션 하에 수행됩니다. (전파 정책: PROPAGATION_REQUIRED)
OuterService는 InnerService가 뱉은 예외를 Consume해야 합니다.
InnerService
InnerService는 엔터티의 InnerCommit을 TRUE로 갱신하는 로직을 수행합니다.
InnerService는 InnerCommit 갱신 직전 또는 직후에 에러를 발생시킬 수 있습니다.
InnerService는 트랜잭션 전파 정책이 변경되며 테스트됩니다.
전파 정책 별 InnerService
자식 트랜잭셔널 메서드의 전파 요구사항을 변경하며 테스트하기 위해
전파 정책별로
InnerService
인터페이스의 하위 구현체를 생성합니다. 그리고@Transactional
로 전파 요구사항을 기입합니다.ex)
칼럼 갱신은 데이터에 직접 접근하는 또 다른
InnserSerivce
구현체에 위임합니다.InnerServiceImpl
테스트 케이스
테스트 실행
https://github.com/caffeine-library/pro-spring-5/blob/main/references/code/binchoo/TransactionPropagationTestProject/src/test/java/org/binchoo/env/propagation/test/PropagationCombinationsTest.java
테스트 오라클
칼럼 값이 TRUE이면, 해당 칼럼은 갱신된 것으로 봅니다. 칼럼 값이 FALSE이면, 아예 갱신 로직에 접근하지 못 했거나 롤백된 것입니다.
assert~Rollback()
메서드는 두 용도로 활용되니 유의하여야 합니다.결과 해석
https://github.com/caffeine-library/pro-spring-5/issues/68#issuecomment-948208664 해당 테스트 수행하여 정리해 본 Propagation 정책의 의미입니다.
@caffeine-library/readers-pro-spring-5