쿠팡이나 네이버 쇼핑 같은 사이트에서 주문을 한다고 하자.
사용자가 제품 한 개를 구매했을 경우, 해당 제품을 Stock이라 하면 Stock의 재고 (quantity)를 하나 감소시키는 로직을 구현하면 될 것이다.
하지만, 만약에 동시에 여러 요청이 들어온다면?
@Test
void 동시에_여러건의_요청() throws InterruptedException {
// 몇번 실행할지 나타내는 변수
int count = 100;
// ExecutorService : 비동기로 실행하는 작업을 단순화하여 사용할 수 있게 도와주는 자바 API
// newFixedThreadPool(int nThreads)
// 초기 스레드 개수 : 0
// 코어 스레드 수와 최대 스레드 수는 매개변수 nThreads
// 스레드 개수보다 작업 개수가 많으면 스레드를 새로 생성
// 일이 할당되지 않은 쓰레드도 제거하지 않는다.
ExecutorService executorService = Executors.newFixedThreadPool(10);
// CountDownLatch : 다른 쓰레드에서 수행하는 작업이 끝날 때까지 대기하게 도와주는 클래스
CountDownLatch countDownLatch = new CountDownLatch(count);
for (int i = 0; i < count; i++) {
executorService.submit(() -> {
try {
// id가 1인 stock 재고 1개씩 감소
stockService.decrease(1L, 1L);
} finally {
countDownLatch.countDown();
}
});
}
// 카운트가 0이되면 대기가 풀리고 이후 스레드가 실행
countDownLatch.await();
Stock stock = stockRepository.findById(1L).orElseThrow();
assertEquals(0, stock.getQuantity());
}
테스트 Fail이 뜨고, 100개가 0개로 되어야하는데 아직 남아있는 재고가 있는 것을 발견할 수 있다.
동시성 이슈를 테스트하기 위해서 IntelliJ 상에서 Spring boot Test를 진행할 것이다.
테스트 하기 전에, Mysql과 Redis에 관한 설정을 Docker를 통해서 진행해보자
Mysql
Redis
Docker Desktop으로 확인해보면 아래와 같이 되어있는 것을 볼 수있다.
