[SSA] 새로운 트랜잭션을 만들면 데드락이 발생하는 것을 테스트해 보자

2025. 8. 29. 03:00·SSA/Back


데드락 테스트 경로

 

 

 

테스트는 2개의 스레드에서 진행합니다.

 

테스트를 위한 퍼블리셔와 리스너 코드입니다.

// 퍼블리셔
@Service
public class DeadLockTestEventProducer {

    private final Logger log = LoggerFactory.getLogger(DeadLockTestEventProducer.class);

    private final ApplicationEventPublisher publisher;

    public DeadLockTestEventProducer(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    @Transactional
    public void publishEvent(TestEvent event) {
        // 코드 작성
        log.info("call DeadLockTestEventProducer.publish(). event-id: {}", event.getId());
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("wake up! event-id: {}", event.getId());
        publisher.publishEvent(event);
    }
}

 

// 리스너
@Service
public class DeadLockTestAfterCommitEventListener {

    private final Logger log = LoggerFactory.getLogger(DeadLockTestAfterCommitEventListener.class);

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) // AFTER_COMMIT -> 기본값이지만 테스트 확인용 명시
    public void handleEvent(TestEvent event) {
        log.info("call DeadLockTestAfterCommitEventListener.handleEvent(). event-id: {}", event.getId());
    }
}

 

 

1. 데드락 발생 테스트

우선 hikariCP 설정을 다음과 같이 진행합니다.

  • MaximumPoolSize = 2 (데드락을 피할 수 있는 최소 pool size 공식: Tn x (cm - 1) + 1 에서 1을 뺀 값)
spring:
  config:
    activate:
      on-profile: requires-new-deadlock-occur

  datasource:
    hikari:
      maximum-pool-size: 2            # 최대 커넥션 수
      connection-timeout: 30000       # 커넥션을 가져올 때 대기할 최대 시간 (밀리초)
      max-lifetime: 1800000           # 커넥션이 유지될 최대 시간 (밀리초)

이후 위의 퍼블리셔와 리스너 코드를 2개의 쓰레드에서 실행합니다.

 

테스트 코드입니다.

@Test
void 데드락이_발생한다() throws InterruptedException {
    // given
    ExecutorService executorService = Executors.newFixedThreadPool(2);
    CountDownLatch latch = new CountDownLatch(2);

    // when
    // 스레드 1: 이벤트 1 발행
    executorService.submit(() -> {
        try {
            deadLockTestEventProducer.publishEvent(new TestEvent(1L));
        } finally {
            latch.countDown();
        }
    });

    // 스레드 2: 이벤트 2 발행
    executorService.submit(() -> {
        try {
            deadLockTestEventProducer.publishEvent(new TestEvent(2L));
        } finally {
            latch.countDown();
        }
    });

    latch.await(); // 두 스레드의 작업이 끝날 때까지 대기

    // then
    // 데드락으로 인해 커넥션 타임아웃 예외가 발생하는 것을 검증
}

 

 

데드락 발생 시나리오 설명

  1. 커넥션 풀: maximum-pool-size: 2 설정으로 DB 커넥션은 2개입니다.
  2. 쓰레드 1 시작: publishEvent(1L) 호출. @Transactional에 의해 커넥션 1을 획득합니다. (남은 커넥션: 1개)
  3. 쓰레드 2 시작: publishEvent(2L) 호출. @Transactional에 의해 커넥션 2를 획득합니다. (남은 커넥션: 0개)
  4. 쓰레드 1 진행: Thread.sleep(2000) 후 publisher.publishEvent(event)를 호출합니다.
  5. 쓰레드 1 블로킹: DeadLockTestEventListener의 handleEvent가 호출됩니다. 이 메서드는 REQUIRES_NEW이므로 새로운 커넥션을 요청합니다. 하지만 남은 커넥션이 없으므로 쓰레드 1은 커넥션 풀에 커넥션이 반납되기를 기다리며 대기(Blocking) 상태가 됩니다.
  6. 쓰레드 2 진행: Thread.sleep(2000) 후 publisher.publishEvent(event)를 호출합니다.
  7. 쓰레드 2 블로킹: DeadLockTestEventListener의 handleEvent가 호출되고, 마찬가지로 새로운 커넥션을 요청합니다. 커넥션 풀이 비어있으므로 쓰레드 2도 대기(Blocking) 상태가 됩니다.
  8. 데드락 !:
    • 쓰레드 1은 커넥션 1을 점유한 채, 다른 커넥션을 기다립니다.
    • 쓰레드 2는 커넥션 2를 점유한 채, 다른 커넥션을 기다립니다.
    • 두 쓰레드 모두 자신이 점유한 커넥션을 해제하지 못하고, 서로가 커넥션을 반납하기만을 무한히 기다리는
      교착 상태(Deadlock)에 빠집니다.

결국 Hikari의 connection-timeout 설정 시간(기본 30초)이 지나면

SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out. 예외가 발생하며

테스트가 실패하게 됩니다. 이 실패를 통해 데드락 발생을 확인할 수 있습니다.

실제 결과

 

 

 

 

---------------------------------------------------

2. 데드락 발생 예방 테스트

  • 쓰레드 개수 2개
  • MaximumPoolSize = 3 (데드락을 피할 수 있는 최소 pool size 공식: Tn x (cm - 1) + 1 )
spring:
  config:
    activate:
      on-profile: requires-new-deadlock-not-occur

  datasource:
    hikari:
      maximum-pool-size: 3            # 최대 커넥션 수
      connection-timeout: 30000       # 커넥션을 가져올 때 대기할 최대 시간 (밀리초)
      max-lifetime: 1800000           # 커넥션이 유지될 최대 시간 (밀리초)

 

이제 1에서와 동일한 코드를 실행시켜 보겠습니다.

실제 결과

데드락이 발생하지 않고 핸들러 코드가 적절히 실행된 것을 확인할 수 있습니다.

 

 

 

'SSA > Back' 카테고리의 다른 글

[SSA] 비동기 이벤트 발행기 테스트  (1) 2025.09.08
[SSA] 알림 모듈 구조 개선 및 간단한 동시성 제어 ..  (2) 2025.08.30
[SSA] 트랜잭션을 새로 만들어서 사용하면 ?  (0) 2025.08.29
[SSA] 500명 알림 발송 6.4초 -> 0.27초, 카프카 컨슈머 병렬 처리 적용기  (3) 2025.08.28
[SSA] 수강 기능 및 알림 기능을 한 곳에서 처리하면 발생할 수 있는 문제  (1) 2025.07.19
'SSA/Back' 카테고리의 다른 글
  • [SSA] 비동기 이벤트 발행기 테스트
  • [SSA] 알림 모듈 구조 개선 및 간단한 동시성 제어 ..
  • [SSA] 트랜잭션을 새로 만들어서 사용하면 ?
  • [SSA] 500명 알림 발송 6.4초 -> 0.27초, 카프카 컨슈머 병렬 처리 적용기
하가네
하가네
  • 하가네
    하 렌
    하가네
  • 전체
    오늘
    어제
    • 분류 전체보기 (128) N
      • Computer Science (27)
        • 운영체제 (7)
        • 데이터통신 (10)
        • 자료구조 (4)
        • 논리회로 (0)
        • 확률 및 통계 (0)
        • 데이터베이스 (2)
        • AI소프트웨어 (3)
        • 컴퓨터네트워크 (1)
      • Design (5) N
        • OOP - 객체 지향 프로그래밍 (3) N
        • DDD - 도메인 주도 개발 (데이터베이스 주도 .. (0)
        • EDA - 이벤트 기반 아키텍처 (1)
        • MSA - 마이크로서비스 아키텍처 (0)
        • ADD - AI 주도 개발 (1)
      • Language (2)
        • Java (0)
        • TypeScript (2)
      • Framework (12)
        • Spring (9)
        • NestJS (3)
      • Engine (3)
        • Elasticsearch (1)
        • GraphQL + Apollo Federation (2)
      • Plugin - Extension (1)
        • VS Code (1)
        • IntelliJ (0)
      • Tips (2)
        • 터미널 명령어 (1)
        • 우분투 명령어 에러 (1)
      • SSA (26)
        • Front (1)
        • Back (23)
        • DB (1)
        • 기획 (1)
      • CNU SW 아카데미 (43)
        • 1주차 (5)
        • 2주차 (5)
        • 3주차 (2)
        • 4주차 (1)
        • 5주차 (3)
        • 6주차 (2)
        • 7주차 (0)
        • 8주차 (2)
        • 9주차 (14)
        • 10주차 (0)
        • 11주차 (1)
        • 12주차 (0)
        • 13주차 (2)
        • 14주차 (2)
        • 15주차(최종 프로젝트) (3)
        • 최종 프로젝트 이후 (1)
      • 모각코 (6)
        • 2023 동계 (6)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    lint-staged
    개발자경험(DX)
    ESLint
    ci/cd
    Husky
    Typescript
    아키텍처
    프론트엔드/백엔드
    생산성
    DX(DeveloperExperience)
    릴리스엔지니어링
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.
하가네
[SSA] 새로운 트랜잭션을 만들면 데드락이 발생하는 것을 테스트해 보자
상단으로

티스토리툴바