Connection is not available, request timed out가 보이면 풀 크기부터 늘리고 싶어집니다. 하지만 이 메시지가 직접 말해 주는 것은 정해진 시간 안에 풀에서 연결을 얻지 못했다는 사실입니다. 연결을 오래 쓰는 쿼리, 반환되지 않는 연결, 한꺼번에 몰린 요청은 모두 같은 대기를 만들 수 있습니다.
작은 H2 데이터베이스와 연결 하나짜리 풀로 현상을 재현해 보겠습니다. HikariCP 7.0.2, H2 2.4.240, JDK 25.0.3에서 세 테스트를 실행했습니다. 로컬 실험에서 원인의 차이를 보는 글이며, 특정 풀 크기를 운영 환경의 정답으로 제시하지는 않습니다.

connectionTimeout은 연결을 빌리는 대기 시간입니다
connectionTimeout은 클라이언트가 풀에서 연결을 얻기 위해 기다리는 시간입니다. SQL 자체의 실행 제한 시간으로 읽으면 안 됩니다. 풀에 쓸 수 있는 연결이 돌아오지 않으면 대기하다가 예외를 받습니다. DB 접속 실패 등으로 연결을 충분히 만들지 못하는 경우도 함께 확인해야 합니다.
HikariCP 공식 설정 문서의 최소값은 250ms이며, 이 실험에서는 300ms로 설정했습니다. 장애를 빨리 재현하려고 줄인 값입니다. 실제 서비스의 제한 시간은 상위 요청의 시간 예산과 DB 동작을 보고 정해야 합니다.
연결 하나를 붙잡아 두면 같은 오류가 납니다
Maven 프로젝트에 com.zaxxer:HikariCP:7.0.2와 com.h2database:h2:2.4.240를 넣고 다음 코드를 실행하면 됩니다. 로그까지 보려면 SLF4J 구현체를 추가합니다. 아래 코드의 main은 풀을 만들고 닫는 것까지 포함합니다.
Java · PoolExample.java
package blog.pool;
import java.sql.Connection;
import java.sql.SQLException;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
public class PoolExample {
public static HikariDataSource createPool(String name, long leakThreshold) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:h2:mem:" + name);
config.setPoolName(name);
config.setMaximumPoolSize(1);
config.setMinimumIdle(1);
config.setConnectionTimeout(300);
config.setLeakDetectionThreshold(leakThreshold);
return new HikariDataSource(config);
}
public static void pause(long millis) throws InterruptedException {
Thread.sleep(millis);
}
public static void main(String[] args) throws Exception {
try (HikariDataSource pool = createPool("demo", 0)) {
try (Connection held = pool.getConnection()) {
try (Connection second = pool.getConnection()) {
throw new IllegalStateException("이 예제에서는 연결을 얻으면 안 됩니다.");
} catch (SQLException ex) {
System.out.println(ex.getClass().getSimpleName());
System.out.println(ex.getMessage());
}
}
try (Connection returned = pool.getConnection()) {
System.out.println("반환 후 다시 획득: " + returned.isValid(1));
}
}
}
}
첫 번째 연결이 대여된 상태에서 두 번째 연결을 요청하므로 대기가 발생합니다. 테스트에서는 SQLTransientConnectionException과 연결 획득 시간 초과 메시지를 확인했습니다. 첫 연결을 닫은 다음에는 다시 연결을 얻고 isValid(1)도 통과했습니다.
HikariCP에서 대여한 연결의 close()는 풀에 연결을 반환하는 흐름으로 연결됩니다. 직접 JDBC를 사용하는 코드라면 try-with-resources로 반환 범위를 분명히 하세요. Spring이 관리하는 트랜잭션 자원은 해당 관리 방식에 맞게 사용해야 합니다.
같은 시각의 풀 상태와 DB 상태를 맞춰 봅니다
| 후보 | 함께 볼 증거 | 먼저 할 일 |
|---|---|---|
| 느린 쿼리·긴 트랜잭션 | active가 높고 대기가 쌓임. DB의 실행 중 SQL·락 대기 또는 앱의 긴 점유 구간 | SQL·락·트랜잭션 범위 확인 |
| 연결 반환 누락 | 요청이 끝난 뒤에도 대여 연결이 줄지 않음. 획득 위치와 반환 경로 불일치 | 예외 분기를 포함해 close 경로 확인 |
| 동시 요청이 처리량을 넘음 | 부하와 대기 증가가 함께 나타남. 작업 종료 후 연결 반환은 정상 | 동시성 제한·DB 여력·인스턴스별 풀 합계 확인 |
이 표는 조사 순서이며 자동 판정식이 아닙니다. active는 대여 중인 연결 수이므로 모두 SQL을 실행 중이라는 뜻은 아닙니다. 연결을 가진 채 외부 API를 기다리는 애플리케이션 코드도 DB 밖에서 시간을 쓸 수 있습니다.
실험에서는 HikariPoolMXBean의 getActiveConnections(), getIdleConnections(), getThreadsAwaitingConnection()를 봤습니다. 연결 하나를 점유했을 때 active 1·idle 0이었고, 별도 스레드가 기다리면 대기 수가 1이 됐습니다. 첫 연결을 반환하자 대기하던 요청이 연결을 얻었습니다. 운영 지표도 오류가 난 시각과 맞춰 읽는 편이 좋습니다.
누수 경고가 나도 연결이 나중에 돌아올 수 있습니다
leakDetectionThreshold는 연결이 풀 밖에 오래 머무르면 가능한 누수를 로그로 알리는 진단 설정입니다. 0이면 비활성이고, 사용할 수 있는 최소 임계값은 2,000ms입니다. 경고만으로 반환 누락이 확정되지는 않습니다.
이를 확인하려고 임계값을 2,000ms로 두고 H2에서 2,600ms 동안 기다리는 함수를 실행했습니다. PoolExample.pause는 위 코드에 있는 메서드입니다.
Java · 느린 쿼리 재현
try (Connection connection = pool.getConnection();
Statement statement = connection.createStatement()) {
statement.execute(
"CREATE ALIAS PAUSE FOR 'blog.pool.PoolExample.pause'"
);
statement.execute("CALL PAUSE(2600)");
}
이번 실행에서는 누수 의심 경고가 먼저 나오고, 연결을 닫은 뒤 풀에 반환됐다는 로그도 나왔습니다. 재현한 것은 오래 점유한 연결도 누수 경고를 만들 수 있다는 점입니다. 실제 장애에서는 획득 스택과 긴 SQL·락 대기·반환 코드를 함께 봐야 합니다.
실제 시간 기반 경고이므로 로그가 찍히는 시각은 실행 환경에 따라 달라질 수 있습니다. 이 실험의 2.6초를 운영 임계값으로 복사하지 말고 정상적인 긴 작업의 범위와 로그 부담을 먼저 확인하세요.
풀을 키우기 전에 연결이 돌아오는 길부터 확인합니다
- 오류 시각의 active·idle·대기 수와 DB 연결 오류 로그를 확보합니다.
- 느린 SQL과 락 대기를 확인하고, 연결을 보유한 채 다른 작업을 기다리는 구간을 찾습니다.
- 직접 획득한 연결의 정상·예외 경로에서 반환이 빠지지 않는지 확인합니다.
- 반환이 정상이라면 동시 요청 제한과 풀 크기를 함께 검토합니다. 애플리케이션 인스턴스가 여러 개면 전체 연결 수를 합산합니다.
공식 풀 크기 설명도 큰 풀이 항상 빠른 것은 아니라고 강조합니다. 풀만 늘리면 DB에 더 많은 동시 작업이 들어갑니다. 연결 대기가 줄었더라도 DB 처리 시간이 늘어 전체 응답이 더 느려지는지 부하 조건을 맞춰 확인해야 합니다.
커넥션 풀의 기본 흐름이 낯설다면 DataSource와 Connection Pool을 정리한 글을 먼저 읽어도 좋습니다. 이번 오류를 볼 때 기억할 질문은 하나입니다. “왜 연결을 빌리지 못했나?” 다음에는 “이미 빌린 연결은 어디에서 시간을 쓰고 있나?”를 이어서 확인해 보세요.
참고한 공식 자료
공식 자료 확인일: 2026년 10월 8일. 제도와 라이브러리의 변경 사항은 신청·적용 시점에 다시 확인해 주세요.
'Spring Data > DataSource와 Connection Pool' 카테고리의 다른 글
| DataSource와 Connection Pool - DataSource 란? (0) | 2022.12.08 |
|---|---|
| DataSource와 Connection Pool - Connection Pool 이란? (0) | 2022.12.07 |
댓글