<aside> 💡 실제 DB 행에 Lock을 걸어서 정합성을 맞추는 방법
</aside>
@Lock(LockModeType.PESSIMISTIC_READ)
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Lock(LockModeType.PESSIMISTIC_FORCE_INCREMENT)
비관적 Lock 전체 코드
결제 할 상품을 찾을 때, 비관적 LOCK을 건다.
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select p from Product p where p.id = :productId")
Optional<Product> findByIdWithPessimistic(@Param("productId") Long productId);
findByIdWithPessimistic를 사용하여 결제할 상품을 DB에서 조회한다.
@Transactional
public int orderProductWithLock(String pgToken, Long memberId)
' ' ' ' ' '
Product product = productRepository.findByIdWithPessimistic(Long.valueOf(productId))
.orElseThrow(() -> new ProductNotFoundException(ErrorCode.PRODUCT_NOT_FOUND));
' ' ' ' ' '
product.saleProduct(); //쿼리 2
paymentHistoryRepository.save(paymentHistory); //쿼리 1
System.err.println("종료");
return product.getStock();
//트랜잭션.커밋
}
비관적 Lock 테스트 코드
// 동시성 테스트 시작 ====================================================================
int threadCount = 3;
ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
CountDownLatch latch = new CountDownLatch(threadCount);
List<Exception> exceptionList = new ArrayList<>();
for (int i = 0; i < threadCount; i++) {
int finalI = i;
executorService.submit(() ->{
try {
paymentService.orderProductWithLock("asg", memberList.get(finalI).getId());
} catch (Exception e) {
e.printStackTrace();
exceptionList.add(e);
} finally {
latch.countDown();
}
});
}
latch.await();
Assertions.assertThat(exceptionList.get(0) instanceof PessimisticLockingFailureException);