Spring Batch 멀티스레드 환경에서 HikariCP Connection Leak 문제 해결 방법

🔹 개요
Spring Batch에서 멀티스레드 환경을 사용한 후 HikariCP의 Connection Leak 경고가 발생하는 문제에 대해 분석하고 해결 방법을 정리합니다.
특히 멀티스레드와 HikariCP의 connection-pool 설정, 트랜잭션 관리 방식, DAO 호출 구조가 Connection Leak에 미치는 영향을 집중적으로 다룹니다.


🔹 HikariCP 설정과 Connection Leak 원인 분석

사용된 HikariCP 설정은 다음과 같습니다.

minimum-idle: 2  
maximum-pool-size: 30
keepalive-time: 120000
max-lifetime: 1800000
idle-timeout: 120000
leak-detection-threshold: 10000
connection-timeout: 15000
validation-timeout: 10000

이 설정에서 Connection Leak이 발생하는 주요 원인은 다음과 같습니다.

1. 멀티스레드 환경에서 DAO 호출 방식에 따른 커넥션 과다 사용

  • 기본적으로 1개의 트랜잭션은 1개의 커넥션을 사용하지만, 트랜잭션이 적절히 관리되지 않으면 DAO 호출마다 새로운 커넥션을 가져오는 경우가 발생.
  • Spring Batch의 Tasklet에서 ExecutorService를 사용하면, 각 스레드에서 별도로 커넥션을 획득할 가능성이 있음.

2. @Transactional이 적용되지 않아 DAO 호출마다 커넥션을 생성하는 경우

  • @Transactional이 적용되지 않으면 각 DAO 메서드가 개별적인 커넥션을 획득하고 반환.
  • 트랜잭션을 유지하지 않으면 스레드 개수(예: 10개)보다 더 많은 커넥션을 사용하여 HikariCP 풀 크기(maximum-pool-size)를 초과할 수 있음.

3. Propagation.REQUIRES_NEW 사용으로 예상보다 많은 커넥션 사용

  • @Transactional(propagation = Propagation.REQUIRES_NEW)을 사용하면, 새로운 트랜잭션을 시작하면서 새로운 커넥션을 가져옴.
  • 한 개의 트랜잭션 안에서 REQUIRES_NEW를 여러 번 사용하면 각 호출마다 새로운 커넥션이 생성될 수 있음.

4. leak-detection-threshold 설정이 너무 짧아 정상적인 트랜잭션도 Leak으로 감지

  • leak-detection-threshold: 10000 (10초) 설정은 10초 내에 커넥션이 반환되지 않으면 Leak 경고를 발생.
  • 트랜잭션이 10초 이상 걸리는 경우에도 불필요한 경고가 발생할 수 있음.

🔹 해결 방법

1. @Transactional을 활용하여 트랜잭션 단위에서 커넥션 관리

  • 트랜잭션 내부에서 여러 DAO 호출을 묶어서 실행하면, 하나의 커넥션만 유지할 수 있음.
@Transactional
public void processOrders() {
orderDAO.updateOrderStatus();
orderDAO.insertOrderLog();
orderDAO.updateStockQuantity();
}

이렇게 하면 여러 DAO 호출이 하나의 트랜잭션 내에서 실행되므로, 불필요한 커넥션 낭비가 줄어듦.


2. HikariCP 설정 조정

최적화된 설정 예시:

maximum-pool-size: 30  
max-lifetime: 900000 # 15분 (Aurora wait_timeout보다 작게)
idle-timeout: 600000 # 10분
leak-detection-threshold: 30000 # 30초
  • leak-detection-threshold30초 이상으로 설정하여 불필요한 Connection Leak 경고 방지.
  • max-lifetimeAurora RDS의 wait_timeout보다 작게 설정하여 안정적인 커넥션 관리.

3. Tasklet 내부에서 멀티스레드 실행 시 트랜잭션 처리

  • Spring Batch에서 Tasklet 내부에서 직접 ExecutorService를 사용하는 대신, Spring의 TaskExecutor를 활용하여 트랜잭션 관리.
@Bean
public Step parallelStep() {
return stepBuilderFactory.get("parallelStep")
.<InputType, OutputType>chunk(10)
.reader(myItemReader())
.processor(myItemProcessor())
.writer(myItemWriter())
.taskExecutor(taskExecutor()) // ✅ 멀티스레드 처리
.throttleLimit(10) // ✅ 최대 10개 스레드
.build();
}

이렇게 하면 각 스레드가 독립적인 트랜잭션을 유지하면서도 커넥션이 안정적으로 관리됨.


🔹 결론

  1. HikariCP Connection Leak 경고의 원인은 멀티스레드 환경에서 커넥션 반환이 제대로 이루어지지 않기 때문.
  2. 멀티스레드 환경에서 @Transactional을 활용하여 커넥션을 효율적으로 관리해야 함.
  3. HikariCP 설정을 최적화하고, leak-detection-threshold 값을 증가시켜 불필요한 경고를 방지해야 함.
  4. Spring Batch의 멀티스레드 처리는 TaskExecutor를 활용하여 트랜잭션과 커넥션을 안정적으로 관리하는 것이 좋음.

이렇게 조정하면, 멀티스레딩 환경에서도 Connection Leak 없이 안정적으로 HikariCP와 Spring Batch를 운영할 수 있습니다! 🚀

관련 글

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다