아래 코드는 org.springframework.boot:spring-boot-autoconfigure 에 기본으로 설정된 쓰레드 풀 관련 필드이다.
public static class Tomcat {
private int maxConnections = 8192;
private int acceptCount = 100;
...
}
public static class Threads {
private int max = 200;
private int minSpare = 10;
...
}
위 설정처럼 spring boot 는 기본적으로 최소 10개, 최대 200개의 쓰레드를 사용할 수 있고, 최대 8192개의 커넥션이 가능하고, 100 size의 큐를 갖고 있다.
그런데, WAS 에서 유저가 얼마 있지도 않음에도 다음과 같이 커넥션을 연결하지 못한 채 타임아웃이 나버렸다.
java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available
기본적으로 HikariCP 는 최대 10개의 DB 커넥션을 지원하고 있다.
하나의 페이지에서 여러 API 를 요청함에 따라 Spring MVC 에서는 요청 별로 쓰레드를 할당하고, nginx를 통해 keep-alive를 90초로 설정해놓았기에
쓰레드에서 커넥션 풀을 잡고 놓아주지 않는 것은 아닐까? 라고 생각했다.
결론
JDBC Template 를 사용하고 있어, Connection 을 언제 꺼내고 반환하는 지 살펴보았다.
template.query, template.update 와 같이 쿼리를 실행할 때 전후로 getConnection 과 closeConnection 을 호출하고 있었다.
따라서 쓰레드가 커넥션을 잡고 있는 것은 아닐 것이라 판단하였다.
keep-alive 를 90초로 설정한 것은 불필요한 handshaking 을 줄여주는 것이지, 쓰레드를 잡고 있는 것이 아니다.
그렇다면 유저도 얼마 없는데 30초 씩이나 DB 커넥션을 얻지 못할 일이 뭐가 있을까?
public static Array createSqlArray(JdbcTemplate template, List<String> tags) throws SQLException {
Array stringArray = null;
try {
stringArray = template.getDataSource().getConnection().createArrayOf("varchar", tags.toArray());
} catch (SQLException e) {
throw new SQLException(e);
}
return stringArray;
}
위 코드는 template 에서 datasource를 직접 꺼내고 직접 커넥션을 얻어 사용하였는데, 끝나고 반환을 하지 않았다.
위 같은 경우에 finally 구문에서 close 를 해주거나, DataSource 를 커스터마이징하여 AutoClosable 를 구현하여 try-with-resources 를 사용하는 방법으로 해결할 수 있다.
근데 그냥 저 코드 자체가 문제인듯