[Java] JPA 낙관적 락(Optimistic) 동시성 제어 - 2

ChangSol·2024년 3월 21일

이전 - [Java] JPA 낙관적 락(Optimistic) 동시성 제어 - 1
위 낙관적 락을 이용하여 버전 충돌 시 OptimisticLockingFailureException 오류가 발생하는 것을 알 수 있었다.

그렇다면 동시성 제어를 완벽히 하기 위해 위 오류를 어떻게 해결해야 할까?
try-catch 로 예외처리를 잡아 무한루프를 돌려도 되겠지만, 좀 더 간결하고 좋은 방법을 고민하다 Spring에서 지원하는 Retry 라이브러리를 채택하였다.

Spring-Retry

Spring Retry는 실패한 작업을 재호출하는 기능을 제공한다.

사용법

  1. Springframwork Spring-Retry를 의존성에 추가한다.
	//Spring Retry
    implementation 'org.springframework.retry:spring-retry'
  1. Srping Retry 활성화
  • @EnableRetry Annotation을 @Configuration 클래스에 추가
@Configuration
@EnableRetry
public class CommonConfig {...}
  1. Retry 사용
  • @Retryable Annotation을 사용
    옵션

따라서 서비스 코드에 아래와 같이 @Retryable 을 추가

@Service
@RequiredArgsConstructor
public class SeqService {

	private final SeqRepository seqRepository;

	@Retryable(maxAttempts = 10, value = {ObjectOptimisticLockingFailureException.class, DataIntegrityViolationException.class}, backoff = @Backoff(delay = 500))
	public String getSeq(Seq.Type type) {
		Long num = this.generationNum(type);
		return String.format("%s-%s", type.name(), StringUtils.leftPad(num.toString(), 6, "0"));
	}

	@Transactional
	Long generationNum(Seq.Type type) {
		Seq seq = seqRepository.findBySeqType(type).orElse(Seq.builder()
															  .seqType(type)
															  .num(0L)
															  .build());
		seq.numUp();
		return seqRepository.save(seq)
							.getNum();
	}
}

@Retryable 옵션

  • maxAttempts : 최대 시도 횟수
  • value : 재시도할 오류 클래스 목록
  • backoff : 시도 시 대기 (millisecond)

위 코드와 같이 Retryable만 붙여주면 아래와 같이 동시성 제어가 통과됨을 볼 수 있다.

DataIntegrityViolationException 를 추가로 재시도하는 이유는 서비스 내에서 orElse를 보면 INSERT도 같이 존재하기에 중복 충돌까지 같이 방지하기 위하여 추가하였다.


profile
Back-End Developer

0개의 댓글