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를 운영할 수 있습니다! 🚀

관련 글

답글 남기기

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