개요
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-threshold
을 30초 이상으로 설정하여 불필요한 Connection Leak 경고 방지.max-lifetime
을 Aurora 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();
}
이렇게 하면 각 스레드가 독립적인 트랜잭션을 유지하면서도 커넥션이 안정적으로 관리됨.
결론
- HikariCP Connection Leak 경고의 원인은 멀티스레드 환경에서 커넥션 반환이 제대로 이루어지지 않기 때문.
- 멀티스레드 환경에서
@Transactional
을 활용하여 커넥션을 효율적으로 관리해야 함. - HikariCP 설정을 최적화하고,
leak-detection-threshold
값을 증가시켜 불필요한 경고를 방지해야 함. - Spring Batch의 멀티스레드 처리는
TaskExecutor
를 활용하여 트랜잭션과 커넥션을 안정적으로 관리하는 것이 좋음.
이렇게 조정하면, 멀티스레딩 환경에서도 Connection Leak 없이 안정적으로 HikariCP와 Spring Batch를 운영할 수 있습니다!