<aside> 💡 DB의 Lock을 사용하지 않고 Version 관리를 통해 애플리케이션 레벨에서 처리
</aside>
Version 관리
상품을 버전별로 만들어서, 재고 수가 줄면 버전을 올린다.
시작할때랑 종료할때 버전 체크
상품 #1 - 재고 : 3 (버전 : 1)
낙관적 Lock 전체 코드
//버전 칼럼 추가
@Version
private Long version;
@Lock(LockModeType.OPTIMISTIC)
@Query("select p from Product p where p.id = :productId")
Optional<Product> findByIdWithOptimistic(@Param("productId") Long productId);
@Transactional
public int orderProduct(String pgToken, Long memberId){
' ' ' ' '
Product product = productRepository.findByIdWithOptimistic(Long.valueOf(productId))
.orElseThrow(() -> new ProductNotFoundException(ErrorCode.PRODUCT_NOT_FOUND));
' ' ' ' '
product.saleProduct(); //쿼리 2
paymentHistoryRepository.save(paymentHistory); //쿼리 1
return product.getStock();
//트랜잭션.커밋
}
}
낙관적 Lock 테스트 코드
// 동시성 테스트 시작 ====================================================================
int threadCount = 3; //3개의 Thread도 테스트 후 점차 늘려나간다.
ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
CountDownLatch latch = new CountDownLatch(threadCount);
List<Exception> exceptionList = new ArrayList<>();
for (int i = 0; i < threadCount; i++) {
executorService.submit(() ->{
try {
paymentService.orderProduct("asg", member.getId());
} catch (Exception e) {
e.printStackTrace();
exceptionList.add(e);
} finally {
latch.countDown();
}
});
}
latch.await();
Assertions.assertThat(exceptionList.get(0) instanceof OptimisticLockingFailureException);
Mock 테스트로는 실제 데이터베이스에 저장되는 로직을 테스트 할 수 없기 때문에 스프링 부트 테스트로
실제 로직과 동일하게 테스트 하도록 변경
@SpringBootTest, @MockBean, Mockito, Junit 등 테스트에 대해서…
deadlock 발생
🔗 https://tecoble.techcourse.co.kr/post/2023-08-16-concurrency-managing/