인프런 커뮤니티 질문&답변

HeeSeok Noh님의 프로필 이미지

작성한 질문수

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

Pessimistic Lock 활용해보기

Pessimistic Lock 전체 테스트 오류 문의

작성

·

87

0

안녕하세요.

Pessimistic Lock 소스에서 각각의 테스트 하나하나는 통과하는데요.

테스트 코드 전체로 돌리면 에러가 나더라고요.

 

public interface StockRepository extends JpaRepository<Stock, Long> {

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("select s from Stock s where s.id = :id")
    Stock findByIdWithPessimisticLock(@Param("id") Long id);
}

 

StockServiceTest.java

package com.example.stock.service;

import com.example.stock.domain.Stock;
import com.example.stock.repository.StockRepository;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
class StockServiceTest {

    @Autowired
    private PessimisticLockStockService stockService;
    @Autowired
    private StockRepository stockRepository;

    @BeforeEach
    public void before() {
        stockRepository.saveAndFlush(new Stock(1L, 100L));
    }

    @AfterEach
    public void after() {
        stockRepository.deleteAll();
    }

    @Test
    @DisplayName("재고1개 감소 테스트")
    public void 재고감소() {
        stockService.decrease(1L, 1L);

        //100 - 1 = 99
        Stock stock = stockRepository.findById(1L).orElseThrow();

        assertThat(stock.getQuantity()).isEqualTo(99L);
    }

    @Test
    @DisplayName("동시에 100개 요청 테스트")
    public void 동시에_100개_요청() throws InterruptedException {
        int threadCount = 100;
        ExecutorService executorService = Executors.newFixedThreadPool(32);
        CountDownLatch countDownLatch = new CountDownLatch(threadCount);

        for (int i = 0; i < threadCount; i++) {
            executorService.submit(() -> {
                try {
                    stockService.decrease(1L, 1L);
                } finally {
                    countDownLatch.countDown();
                }
            });
        }
        countDownLatch.await();
        Stock stock = stockRepository.findById(1L).orElseThrow();
        assertThat(stock.getQuantity()).isEqualTo(0L);
    }
}
image.png

이런 오류가 나는데 @BeforeEach에서 새로 등록하고

@AfterEach에서 delete를 해줘서 각각의 테스트에 영향이 없을거 같은데 왜 나는지 잘 모르겠어요 ㅠ

 

 

github : https://github.com/nhs0912/stock

답변 3

0

최상용님의 프로필 이미지
최상용
지식공유자

HeeSeok Noh 님 안녕하세요.

1) 데이터베이스의 설정이 auto_increment 로 되어있기때문에 그럴겁니다.

2) 테스트를 시작하기전에 findByFirst 를 하여 id 를 얻어온 후 그 값을 사용하면 될것같습니다.

감사합니다.

0

HeeSeok Noh님의 프로필 이미지
HeeSeok Noh
질문자

질문 검색 해보니 다른 답변에서 테스트를 2개 돌리면 Auto_increment 때문에 두번째 테스트에서 id가 2라서 안된다는 답변을 들었는데요.

1)

@BeforeEach에서 매번 테스트마다 id 1L을 새로 정의하고 있는데 auto_increment가 왜 되는걸까요? 그리고 @AfterEach를 통해서 Delete가 되서 다시 id = 1이 넣어질거 같은데 말이죠..

2) 그렇다면 auto_increment를 하면서도 전체테스트가 되려면 어떻게 해야할까요?

0

안녕하세요, 인프런 AI 인턴입니다. HeeSeok Noh님.

동시성 테스트가 부분적으로 실패하는 문제에 대해 도움을 드릴 수 있도록 비슷한 이슈를 다룬 답변을 찾아보았습니다. 두 테스트를 개별적으로 실행하면 문제가 없지만, 전체 테스트 실행 시 오류가 발생하는 상황은 종종 간과되는 동시성 문제나 트랜잭션 관리의 문제일 수 있습니다. 다음은 관련된 몇 가지 주요 원인과 해결책이 담긴 각 질문들입니다:

  1. PESSIMISTIC_WRITE 테스트 시 테스트 클래스 위 @Transactional

    • 출처: 질문에서 @Transactional 어노테이션을 붙이면 첫 스레드풀 사이즈 만큼의 쿼리만 나가고 계속 대기 중인 문제.
    • 설명: 트랜잭션과 락 관리에서 발생할 수 있는 문제로, 모든 테스트가 하나의 트랜잭션에서 실행되어 다른 테스트의 영향을 받는 경우가 있습니다. 이를 해결하려면 테스트에 적절한 트랜잭션 관리가 필요합니다.
  2. 테스트 수행 결과가 항상 에러가 발생합니다.

    • 출처: 여러 스레드를 사용한 동시성 테스트에서 예상된 결과값과 실제 결과값의 불일치 문제.
    • 설명: ExecutorServiceCountDownLatch를 사용하는 경우, 동시성 처리에서 발생할 수 있는 이슈를 다루고 있습니다.
  3. pessimistic lock이 동작하지 않습니다.

    • 출처: Pessimistic Lock 설정 문제와 쿼리 실행의 일관성 이슈.
    • 설명: Pessimistic Lock이 제대로 동작하지 않아서 발생할 수 있는 여러 문제를 설명합니다.

각각의 링크에서 제공된 답변을 통해 문제의 근본 원인과 해결 방안을 파악할 수 있을 것입니다.

이미지 분석에서 NullPointerException이 발생하는 코드 부분을 보면, 트랜잭션 범위 내에서 관리되지 않아서 발생하는 문제일 가능성을 제시하고 있습니다. 예를 들어, @BeforeEach@AfterEach에서 트랜잭션이 제대로 관리되지 않으면, 테스트 데이터가 정확하게 초기화되지 않거나 삭제되지 않을 수 있습니다.

이와 유사한 문제를 겪고 계신 다른 수강생의 질문과 답변을 참고하셔서 문제를 해결하는 데 도움이 되길 바랍니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.