728x90
1. 개요
아래 코드의 목적은 격리 수준 REPEATABLE_READ를 실제로 테스트 해보는 것이다.
기대했던 결과는 이렇다.
1) updateDuringSelectTwice 메서드에서 Toy 데이터를 조회한다.
2) 중간에 새로운 트랜잭션을 생성하고 해당 Toy 데이터를 수정한다.
3) 다시 똑같은 Toy 데이터를 조회하는데, 격리수준 REPEATABLE_READ이므로 name이 수정된 데이터가 아닌 트랜잭션이 시작할 때 당시의 데이터가 조회될 것이다.
@Slf4j
@Service
public class Service {
private final ToyRepository toyRepository;
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void updateDuringSelectTwice() {
Toy toy = toyRepository.findById(5L)
.orElseThrow(() -> new IllegalArgumentException("not found toy"));
log.info("first select toy : {}", toy.getName());
updateName(UUID.randomUUID().toString().substring(0, 5));
Toy toy2 = toyRepository.findById(5L)
.orElseThrow(() -> new IllegalArgumentException("not found toy"));;
log.info("second select toy : {}", toy2.getName());
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateName(String name) {
Toy toy = toyRepository.findById(5L)
.orElseThrow(() -> new IllegalArgumentException("not found toy"));
toy.setName(name);
toyRepository.save(toy);
log.info("update toy : {}", toy.toString());
}
}
2. 문제 상황
그러나 3) 과정에서 조회한 Toy 데이터의 name은 newToy가 나왔다. (원래 name = toy, 수정된 name = newToy 라고 가정)
지인에게 물어보니 AOP self invocation 문제라고 한다.
간단하게 문제를 정리하면 이렇다.
- Self Invocation 문제는 클래스 내부의 메서드가 다른 메서드를 호출할 때 발생
- Spring AOP는 프록시 기반으로 동작하는데, 같은 클래스 내에서의 메서드 호출은 프록시 객체를 경유하지 않고 직접 호출되며, 이로 인해 AOP가 적용되지 않는다.(@Transactional 이 대표적)
3. 해결 방법
두 개의 메서드를 서로 다른 클래스로 분리해주면 간단히 해결된다.
현상을 체험하며 느낀점은 AOP라는 기술적 특성으로 인해 발생한 문제지만, 관심사 분리라는 키워드도 함께 조명하게 되었다.
새로운 트랜잭션을 분리해서 처리한다는 것은 기존 메서드와 다른 관심사이므로 분리하는 것이 마땅하다는 개념적 측면도 생각하게 되었다.
단순 테스트 연습이라고 편하게 가려다 새로운 문제를 경험했다.
728x90
'트러블슈팅' 카테고리의 다른 글
| 동일 Transaction 내 롤백 마크로 인한 장애 전파 (0) | 2024.07.28 |
|---|