A 서버에서 Insert / Update 로직(Writer DB) 로직 수행 후에 B 서버에서 Select(Read DB) 로직이 수행되는 경우, Replica Lag 현상이 발생할 수 있다.
Replica Lag
복제 지연은 슬레이브(또는 보조)가 마스터(또는 기본)에서 발생하는 업데이트를 따라잡을 수 없을 때 발생한다. 적용되지 않은 변경 사항은 슬레이브의 릴레이 로그에 누적되며, 슬레이브에 대한 데이터베이스 버전은 마스터와의 차이는 점점 커진다.
즉, 슬레이브의 마스터 복제가 지연되는 것이다.
이를 방지하기 위해, A 서버에서 Read DB 에 데이터 복제가 잘 떠졌는지 확인 후 응답하는 로직을 추가하려고 한다.
readOnly = true 로 세팅하면 replica로 연결이 된다는 것을 알았으니, 이를 활용하여 replica 에 복제가 되었는지 확인한다.
@Transactional
public void save(long memberNo) {
memberRepository.save(memberNo);
}
@Transactional(readOnly = true)
public void checkReplica(long memberNo) {
int retryCount = 1;
while (retryCount <= 3) {
log.info("[checkReplica] memberNo: {}, count: {}", memberNo, retryCount);
if (memberRepository.findByMemberNo(memberNo).isPresent()) {
return;
}
retryCount++;
}
}
save
로직이 수행된 후에 checkReplica
로직을 통해 최대 3번까지 replica DB 에 데이터가 복제되었는지 확인한다. 자가호출 방지를 위해 위 서비스로직들을 다른 클래스에서 호출하는 방식이다.
분명히 다른 트랜잭션인데, 왜 save 로직에서 발생하는 select 쿼리 한번밖에 안 나갈까?!
spring.jpa.open-in-view:true
우리 서비스에서는 위 설정을 기본값인 true 로 두고 사용하고 있었는데, 이 설정에 따른 특징은 다음과 같다.
OSIV (Open Session In View) - true 설정
특징 | 설명 |
---|---|
영속성 컨텍스트 활성화 | DB 트랜잭션 시작 시 JPA 영속성 컨텍스트가 DB 커넥션을 가져옴 |
커넥션 유지 | @Transactional 메서드 벗어나도 커넥션을 계속 유지 |
지연 컨텍스트 유지 | 응답 및 렌더링 시 영속성 컨텍스트 유지하여 지연 로딩 가능 |
실시간 트래픽 이슈 | 커넥션 리소스 부족으로 인한 장애 가능성 증가 |
외부 API 호출 문제 | 외부 API 호출 시 커넥션 리소스 반환 지연 문제 |
지연 로딩 활용 가능 | 영속성 컨텍스트가 살아있어야 가능한 지연 로딩 활용 |
OSIV (Open Session In View) - false 설정
특징 | 설명 |
---|---|
영속성 컨텍스트 종료 | 트랜잭션 종료 시 영속성 컨텍스트 닫고 DB 커넥션 반환 |
커넥션 최적화 | 커넥션 리소스 낭비 없이 효율적으로 사용 가능 |
지연 로딩 제약 | 모든 지연 로딩을 트랜잭션 내에서 처리해야 함 |
유연한 사용 | 사용자 요청에 따라 커넥션을 유연하게 사용 |
지연 로딩 처리 | 지연 로딩을 위해 트랜잭션 내에서 명시적으로 처리 필요 |
해당 설정을 false 로 수정할 수 있지만, 어떤 사이드이펙이 있을지 모른다. 코드를 수정하자.
@Transactional
public void save(long memberNo) {
memberRepository.save(memberNo);
}
@Transactional(readOnly = true)
public void checkReplica(long memberNo) {
int retryCount = 1;
while (retryCount <= 3) {
log.info("[checkReplica] memberNo: {}, count: {}", memberNo, retryCount);
entityManager.clear();
if (memberRepository.findByMemberNo(memberNo).isPresent()) {
return;
}
retryCount++;
}
}
checkReplica
내에 존재하는 select 쿼리
가 매번 나가도록 영속성 컨텍스트를 초기화하는 코드를 추가하였다.
위 로직 및 설정을 추가하면 replica lag 은 해결되지만, 히스토리를 모르면 코드 파악이 어려워서 A 서버를 호출하는 B 서버에서 Write / Read DB 를 선택하는 코드를 추가하였다.
A 서버를 호출하는 경우에는 Write DB 를 선택하여 Replica Lag 현상이 일어나지 않도록 변경하였다.