728x90
반응형
JBoss Tech Tips #3 Database Connection / Statement / ResultSet 누수 검출
 
이번 주에는 JBoss에서 기본 설정으로 제공하고 있는 Database Connection / Statement / ResultSet 누수 검출에 관련된 설정을 살펴보겠습니다.
 
JBoss 설치 후 어플리케이션 디플로이시 아래 메시지를 보셨던 분들이 계실 겁니다.
 
유형1) INFO [CachedConnectionManager] Closing a connection for you. Please close them yourself
유형2) WARN [WrappedConnection] Closing a statement you left open, please do your own housekeeping
유형3) WARN  [WrappedStatement.java:878] Closing a result set you left open! Please close it yourself.
 
위 메시지들은 어플리케이션에서 Connection / Statement / ResultSet을 사용 후 close()하지 않았을 때 JBoss에서 자동으로 close()하면서 남기는 로그입니다.
실제 메시지 로그에는 어디서 발생했는지 알 수 있도록 StackTrace가 추가되어 있습니다.
 
이 경우 해당 프로그램을 수정하라고 개발자에게 알려줘야 합니다.
경우에 따라서는 개발자는 전혀 문제되는 것 없다고 할 수 있습니다. 그러면 할 수 없이 서비스를 그냥 놔두거나 안되면 직접 소스를 까봐야 합니다.
보통 문제가 없다고 하는 경우는 코드상에서 실제로  close()를 호출하고 있을텐데요.
아래 사항을 확인해봐야 합니다.
실제 얻어온 connection 개수와 close한 connection의 개수가 같은지  (getConnection() / con.close())
실제 생성한 Statement 개수와 close한 Statement의 개수가 같은지      (con.createStatement() / stmt.close())
실제 쿼리한 ResultSet 개수와 close한 ResultSet의 개수가 같은지       (stmt.executeQuery() / rs.close())
잘못된 코드를 보면 아래와 같은 경우가 많습니다.
case 1)
Statement stmt = null;
try {
    ... ...
   stmt = con.createStatement();
   ... ...
   stmt = con.createStatement();
   ... ...
} catch (SQLException e) {
   ... ...
} finally {
   if (stmt != null) try { stmt.close() } catch (Exception e) {}
   if (con != null) try { con.close() } catch (Exception e) {}
}
finally에서 statement를 닫고 있지만 실제로는 stmt 객체가 두 개인데 하나만 close함
case 2)
PreparedStatement stmt = null;
ResultSet rs = null;
try {
    ... ...
    for (...; ...; ...) {
       stmt = con.prepareStatement("... ...");
       stmt.setString(1, "...");
       rs = stmt.executeQuery();
       ... ...
   }   
 
} catch (SQLException e) {
   ... ...
} finally {
   if (rs != null) try { rs.close() } catch (Exception e) {}
   if (stmt != null) try { stmt.close() } catch (Exception e) {}
   if (con != null) try { con.close() } catch (Exception e) {}
}
finally에서 statement와 resultset을 닫고 있지만 실제 statement 객체와 esultset 객체는 for 문 수만큼 생성되는데 비해 하나씩만 close함
case 3)
PreparedStatement stmt = null;
ResultSet rs = null;
try {
    ... ...
   stmt = con.prepareStatement("... ...");
   for (...; ...; ...) {
       stmt.setString(1, "...");
       rs = stmt.executeQuery();
       ... ...
   }   
 
} catch (SQLException e) {
   ... ...
} finally {
   if (rs != null) try { rs.close() } catch (Exception e) {}
   if (stmt != null) try { stmt.close() } catch (Exception e) {}
   if (con != null) try { con.close() } catch (Exception e) {}
}
finally에서 resultset을 닫고 있지만 실제 resultset 객체는 for 문 수만큼 생성되는데 비해 하나만 close함
 
 
 
더이상 위의 메시지가 나오지  않는다면 누수 검출 서비스는 필요치 않을 것입니다.
누수 검출을 위해서 내부적으로 하는 일이 꽤 있기 때문에 운영에 들어가거나 BMT 시에는 이 서비스를 꺼주는 것이 좋습니다.
그러면 먼저 누수 검출 서비스가 어디에 설정되어 있는지 부터 알아야 겠지요.
 
 
 
Debug가 true일 때 Connection 누수에 대한 검출 서비스가 실행됩니다. 끄고 싶으면 이를 false로 변경해줍니다.
 
Statement / ResultSet의 경우, 각 데이터소스 설정 파일 (*-ds.xml)에서 아래와 같이 설정됩니다.
 <track-statements>true</track-statements>
 
주의할 점은 <track-statements>가 없더라도 기본값이 nowarn이기 때문에 로그상에는 나타나지 않지만 내부적으로 실행되고 있다는 점입니다.
따라서 끄고 싶으면 <track-statements>false</trace-statements>로 명시해줘야 합니다.
 
참고로 Database Connection 누수 검출 서비스를 위와 같이 설정했음에도 동작하지 않는 경우가 있습니다.
누군가 다른 설정을 건드렸기 때문인데요.
웹 어플리케이션의 경우에는 deploy/jboss-web.deployer/server.xml의 <Host>에 CachedConnectionValve가 설정되어 있어야 합니다.
보통 Virtual Host 설정을 하면서 <Host>를 추가할 때 이를 빼먹는 경우가 많습니다.
 
 
그리고, CachedConnectionValve는 CachedConnectionManager 서비스에 의존하기 때문에 JBossWeb의 jboss-service.xml에 이 의존관계가 설정되어 있어야 합니다.
 
 

 

728x90
반응형
블로그 이미지

nineDeveloper

안녕하세요 현직 개발자 입니다 ~ 빠르게 변화하는 세상에 뒤쳐지지 않도록 우리모두 열심히 공부합시다 ~! 개발공부는 넘나 재미있는 것~!

,