동시성 이슈 - 초기 설정

more·2023년 8월 30일

동시성이슈

목록 보기
1/4

동시성 이슈

쿠팡이나 네이버 쇼핑 같은 사이트에서 주문을 한다고 하자.
사용자가 제품 한 개를 구매했을 경우, 해당 제품을 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개로 되어야하는데 아직 남아있는 재고가 있는 것을 발견할 수 있다.

Why

  • Race Condition 문제
    - 둘 이상의 스레드가 공유 데이터에 엑세스하여 동일한 데이터를 변경하려고 할 때 발생
  • A라는 사용자가 재고 100인 물건을 골라 commit되기 전에, B라는 사용자가 재고가 100인 상태에서 commit하려고 한다면, 서로가 100인 상태에서 commit을 했기 때문에 100 - 2 = 98 이 되는 것이 아니라 100 - 1 = 99 가 되어버리는 것

결론

  • Race condition이 발생하지 않도록, A가 접근한 상태라면 commit 할때까지 다른 사용자 (B) 가 해당 데이터에 접근 할 수 없도록 해야한다.

초기 셋팅

  • 동시성 이슈를 테스트하기 위해서 IntelliJ 상에서 Spring boot Test를 진행할 것이다.

  • 테스트 하기 전에, Mysql과 Redis에 관한 설정을 Docker를 통해서 진행해보자

  • Mysql

    • docker 상에서 mysql 이미지는 받아지는데 실행이 안되신다면 3306포트를 사용가능한 다른 포트로 설정해야한다.
  • Redis

    • docker pull redis
    • docker run --name myredis -d -p 6379:6379 redis
  • Docker Desktop으로 확인해보면 아래와 같이 되어있는 것을 볼 수있다.

참고

재고시스템으로 알아보는 동시성이슈 해결 방법

0개의 댓글