강의

멘토링

로드맵

Inflearn brand logo image

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

작성자 없음

작성자 정보가 삭제된 글입니다.

멀티 스레드 테스트 문제

작성

·

30

0

안녕하세요 선생님들 도움을 요청 하고자 질문글을 올립니다.

멀티 스레드 테스트 중 문제가 발생 했습니다.

서칭을 해보아도 문제 해결을 할 수 없어서 질문 드립니다.

 

멀티 스레드 모임 참가 테스트를 위해 테스트 코드를 짯습니다.

트랜잭션 어노테이션이 있을 경우 무한 대기에 빠지고 밑의 예외가 발생 합니다.

이상한점은 트랜잭션 어노테이션이 없거나 H2 DB 테스트를 하면 문제가 없습니다.

완성된 테스트도 H2 데이터 베이스는 통과 하지만 MySql 테스트를 하면 똑같이 무한 대기후

예외가 발생합니다. 이유가 뭘까요 ㅠㅠ 힌트라도 주시면 감사합니다.

void joinEventWhenParticipateAtTheSameTimeWithConcurrency() throws Exception {
    //given
    int taskCount = 5;
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    CountDownLatch countDownLatch = new CountDownLatch(taskCount);

    Event findEvent = executorService.submit(() ->
            eventRepository.save(createEvent("테스터", "테스트 모임", 3))).get();

    executorService.shutdown();
}

java.util.concurrent.ExecutionException: org.springframework.dao.PessimisticLockingFailureException: could not execute statement
[Lock wait timeout exceeded; try restarting transaction]
Caused by: org.springframework.dao.PessimisticLockingFailureException: could not execute statement [Lock wait timeout exceeded; try restarting transaction]


@DisplayName("5명의 회원이 동시에 최대 인원이 3명인 모임에 참가 할때 3명만 참여 할 수 있다.")
@Test
void joinEventWhenParticipateAtTheSameTimeWithConcurrency() throws Exception {
        //given
        int taskCount = 5;
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        CountDownLatch countDownLatch = new CountDownLatch(taskCount);

        Event findEvent = executorService.submit(() ->
                eventRepository.save(createEvent("테스터", "테스트 모임", 3))).get();

        List<User> users = executorService.submit(() -> Stream
                .generate(() -> {
                    User user = createUser("테스터", "testEmail");
                    userRepository.save(user);
                    return user;
                })
                .limit(taskCount)
                .toList()).get();

        //when
        AtomicInteger exceptionCount = new AtomicInteger(0);

        for (User user : users) {
            executorService.submit(() -> {
                try {
                    eventService.joinEvent(findEvent.getId(), user.getId());
                } catch (BusinessException ex) {
                    exceptionCount.incrementAndGet();
                } finally {
                    countDownLatch.countDown();  // 카운트다운
                }
            });
        }
        countDownLatch.await();

        Long participateCount = executorService.submit(
                () -> userEventRepository.countParticipantByEventId(findEvent.getId())).get();

        executorService.shutdown();

        //then
        assertThat(participateCount).isEqualTo(3);
        assertThat(exceptionCount.get()).isEqualTo(2);
    }

답변 1

0

안녕하세요, 인프런 AI 인턴입니다.
조희제님, 멀티 스레드 테스트 관련 문제로 고민이 많으신 것 같습니다. 설명해주신 내용을 바탕으로, 문제의 원인에 대해 생각해보겠습니다.

멀티 스레드 환경에서 트랜잭션과 데이터베이스의 잠금 문제는 자주 발생하는 문제 중 하나입니다. 현재 상황에서는 PessimisticLockingFailureException이 발생하면서 Lock wait timeout exceeded가 출력되고 있습니다. 이는 트랜잭션이 잠금을 획득하려고 하지만, 다른 트랜잭션 때문에 잠금이 장시간 해제되지 않아 시간이 초과되었음을 의미합니다.

몇 가지 가능한 원인과 해결 방법을 제시해드리겠습니다.

  1. 트랜잭션 전파 수준 및 격리 수준 확인:

    • 스프링의 트랜잭션 전파 속성과 격리 수준이 MySQL의 동작에 영향을 미칠 수 있습니다. 트랜잭션 메서드의 전파 속성과 격리 수준을 점검해 보시고, 필요에 따라 변경해 보세요.
  2. 데드락(deadlock) 가능성 탐지:

    • 데드락이 발생했는지 로그를 통해 확인해 보세요. MySQL 콘솔에서 SHOW ENGINE INNODB STATUS; 명령을 실행하여 현재 잠금 상황을 확인할 수 있습니다.
  3. 쿼리 실행 시간 및 데이터베이스 설정 조정:

    • MySQL의 innodb_lock_wait_timeout 설정을 늘려서 테스트하거나, 테스트 환경을 보다 잘 시뮬레이션할 수 있도록 설정을 조정할 수 있습니다.
  4. 테스트 데이터베이스 구성 확인:

    • H2와 MySQL의 데이터베이스 설정 간에는 차이가 있을 수 있습니다. 예를 들어, H2는 종종 자동으로 더 느슨한 잠금 설정을 사용하기 때문에 해당 정보도 검토해 보는 것이 좋습니다.
  5. 비관적 잠금 설정 재확인:

    • PessimisticLockingFailureException이 발생하므로 비관적 잠금 설정 및 관련 쿼리를 확인하여 불필요한 잠금이 걸리지 않도록 조정할 필요가 있을 수 있습니다.

주어진 정보와 가능성을 생각해 보았을 때, 데이터베이스의 잠금 설정 및 트랜잭션 관리가 주요 원인일 가능성이 높습니다. 위의 제안들을 참고하시어 문제를 해결하는 데 도움이 되길 바랍니다. 추가적인 정보나 결과를 알려주시면 보다 구체적으로 도와드리겠습니다. 화이팅입니다!

출처: Spring Boot Reference Documentation

작성자 없음

작성자 정보가 삭제된 글입니다.

질문하기