현재 시스템은 readerDB와 writerDB로 나뉘어져 있습니다.
부하를 분산시키기 위해서입니다.
https://orange-makiyato.tistory.com/75
한 API를 개발해야하는데 5개의 스텝중에 중간에 한 스텝만 save를 하는 로직입니다. 이걸 위해서 5개의 스텝을 모두 readerDB를 타게 하자니, 하루 평균 500만 call이 들어오는 API라 writerDB의 성능 저하가 우려되는 상황입니다.
그래서 처음엔 아래와 같이 작성하였습니다.
@Service
@Transactional(readOnly = true)
public class Testservice {
public String testMothod() {
// 1번 스텝. Select
// 2번 스텝. Select
// 3번 스텝. Insert!!
testDomainService.saveData();
// 4번 스텝. Select
// 5번 스텝. Select
return "";
}
}
@Component
public class TestDomainService {
@Transactional(readOnly = false)
public void saveData() {
// save 로직
}
}
@Transactional(readOnly = true)는 읽기 전용 트랜잭션으로, 데이터 변경 작업을 허용하지 않습니다.
Testservice 클래스를 @Transactional(readOnly = true) 로 하여 전체 로직을 기본적으로 rederDB로 가게 하되 TestDomainService의 saveData() 메서드만 @Transactional(readOnly = false)로 하여 writerDB로 가게 의도하였습니다.
하지만 테스트를 해보니 DB에 저장이 되지 않았습니다.
그래서 확인을 위해 이것저것 출력해보았습니다.(잘 몰라서 그냥 여러가지 출력해보았습니다.)
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
log.debug(">>> save 메소드 안 readOnly: {}", readOnly);
String currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.debug(">>> save 메소드 안 currentTransactionName: {}", currentTransactionName);
Map<Object, Object> resourceMap = TransactionSynchronizationManager.getResourceMap();
log.debug(">>> resourceMap: {}", resourceMap);
TransactionStatus transactionStatus = TransactionAspectSupport.currentTransactionStatus();
log.debug(">>> transactionStatus: {}", transactionStatus);
try {
Connection connection = DataSourceUtils.getConnection(dataSource);
String DBurl = connection.getMetaData().getURL();
log.debug(">>> DBurl: {}", DBurl);
} catch (Exception e) {}
예상하기론 testMothod() 안에서 출력한 것과 saveData() 메서드 안에서 출력한 것은 내용이 달라야 하는데 readOnly 도 똑같이 true 였고 DB url도 역시 readerDB를 연결하고 있었습니다.
이유를 알아보니 @Transactional의 propagation(전파속성) 옵션이 기본값인 Propagation.PROPAGATION_REQUIRED로 설정되어 있기 때문이었습니다. 기본적으로 Spring의 @Transactional 에서 Propagation 옵션을 명시하지 않으면 Propagation.PROPAGATION_REQUIRED가 적용됩니다. 이 옵션은 이미 시작된 트랜잭션이 있으면 해당 트랜잭션을 사용하고, 없으면 새로운 트랜잭션을 시작하는 것입니다. 그래서 Testservice 클래스에서 생성한 트랜잭션이 있기 때문에 그것을 그대로 사용하였고 readOnly = true 였기 때문에 saveData()가 커밋되지 않았던 것입니다.
그래서 saveData()의 @Transactional을 아래와 같이 수정하였습니다.
@Component
public class TestDomainService {
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void saveData() {
// save 로직
}
}
Propagation.REQUIRES_NEW 는 기존 트랜잭션과는 상관없이 항상 새로운 트랜잭션을 만드는 옵션입니다. 그래서 새로운 트랜잭션이 readOnly = false로 만들어져서 해당 로직은 writerDB로 수행이 되어 정상적으로 save가 됩니다.
Transaction의 전체적인 생성, 종료를 보면 다음과 같습니다.
'SpringBoot' 카테고리의 다른 글
REST API request별 swagger3.0.0 파라미터 명세 (0) | 2023.11.29 |
---|---|
@RequiredArgsConstructor 와 @AllArgsConstructor (0) | 2023.11.15 |
웹 개발 발전 과정 Servlet, JSP, MVC, FrameWork (0) | 2023.01.18 |
@NotNull, @NotEmpty, @NotBlank 문자열 검증 (0) | 2022.08.30 |
Java Mockito when(), BDDMokito given() (0) | 2022.08.08 |
댓글