Утечка соединения HikariCP с использованием развернутого oracle.jdbc.driver.T4CConnection

У меня возникла проблема с утечкой ресурсов при использовании HikariCP с развернутым соединением. Небольшое пояснение, затем код.

Я должен использовать развернутое соединение для доступа к методам Oracle для oracle.sql.BFILE. Это потоковая передача двоичного файла из каталога Oracle.

Пример источника данных:

private static DataSource unwrapDatasource;
public static synchronized DataSource getUnwrappedDataSource() {
    if (unwrapDatasource == null) {
        HikariConfig config = new HikariConfig();
        config.setMaximumPoolSize(50);
        config.setLeakDetectionThreshold(120000);
        config.setJdbcUrl(DATABASEURL);
        config.addDataSourceProperty("user", USERNAME);
        config.addDataSourceProperty("password", DBPASSWORD);
        config.addDataSourceProperty("driverType", "thin");
        config.setDriverClassName("oracle.jdbc.pool.OracleDataSource");
        config.setMaxLifetime(300000);
        config.setPoolName("UNWRAP");
        unwrapDatasource = new HikariDataSource(config);
    }

    return unwrapDatasource;
}

  public static Connection getUnwrappedConnection() {
    Connection con = null;
    try {
        con = this.getUnwrappedDataSource().getConnection().unwrap(oracle.jdbc.driver.OracleConnection.class);
    } catch (SQLException ex) {
        //logger junk ommitted for brevity
    }
    return con;
}

Использование HikariCP-java7-2.4.12, поскольку приложение очень старое и работает на tomcat-6. Вот пример соединения, которое сообщается как утечка с

    com.zaxxer.hikari.pool.ProxyLeakTask.run(poolProxyLeak.java:91) : <Connection leak detection triggered for {}, stack trace follows>
java.lang.Exception: Apparent connection leak detected
    at myapp.package.obfuscated.getUnwrapConnection(DataSourceConstants.java:253)
    at myapp.package.obfuscated.BB.execute(DatabaseCallingClass2.java:106)
    at myapp.package.obfuscated.BB.execute(DatabaseCallingClass2.java:85)
    at myapp.package.obfuscated.AA.execute(DataBaseCallingClass.java:52)
    at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:425)
    at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:228)
    at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1913)
    at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:462)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:643)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:723)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.displaytag.filter.ResponseOverrideFilter.doFilter(ResponseOverrideFilter.java:123)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:563)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:610)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:503)
    at java.lang.Thread.run(Thread.java:745)

Вот соединение и как оно используется:

     Connection con = null;
     PreparedStatement pstmt = null;
     OracleResultSet rs = null;
     InputStream inputStream = null;
     StringBuilder stringBuilder = new StringBuilder(128);
     try {
            con = DataSourceConstants.getUnwrappedConnection();

            pstmt = con.prepareStatement("SELECT BFILENAME('ORACLE_DIR', 'fileDownload.12345') AS BFILE from dual" );  //hardcoded for example
            rs = (OracleResultSet) pstmt.executeQuery();

            rs.next();  //Assumption if rs.next() blows up, catch would grab it and resources would all attempt to close in finally block

            bfile = rs.getBFILE(1);
            bfile.open();
            inputStream = bfile.getBinaryStream();
            char c;
            long size = bfile.length();

            int i = 0;
            do {
                c= (char) inputStream.read();
                stringBuilder.append(c);
            } while (++i < size);

            } catch (Exception ex) {
              //logger ommitted but not throwing here
            } finally {
             //cleanup resources
             try { inputStream.close(); } catch (Exception ex) {}
             try { bfile.close(); } catch (Exception ex) {}
             try { rs.close(); } catch (Exception ex) {}
             try { pstmt.close(); } catch (Exception ex) {}
             try {con.close();  } catch (Exception ex){}
            }
       //use stringBuilder later in built output.

Таким образом, приведенное выше извлекает развернутый T4CConnection, чтобы я мог использовать OracleResultSet и BFILE/getBFILE(). Это работает, и я получаю желаемый результат, однако два отдельных класса, использующих эту структуру для извлечения BFILE, являются утечками, и другой метод, который не использует BFILE, но использует развернутое соединение для использования OracleCallableStatement, также дает утечку. Я создал 3 источника данных, чтобы разделить все коннекторы по типу/функции, единственный пул, в котором происходит утечка, — это тот, который использует Datasource.getConnection().unwrap(oracle.jdbc.driver.OracleConnection.class) для своих коннекторов.

Я делаю что-то не так с развернутым соединением? Почему только это течет? Это известная ошибка драйвера? (Я не придумал никаких статей об этом) BFILE, кажется, не очень популярен...


person JustinL    schedule 15.06.2017    source источник
comment
Отличный эзотерический вопрос. Была такая же проблема, ваш вопрос помог мне ее решить.   -  person Somaiah Kumbera    schedule 06.10.2020


Ответы (1)


То, что вы делаете в getUnwrappedConnection(), не то, что вы должны делать: если вы разворачиваете, вам нужно убедиться, что вы также удерживаете соединение пула соединений, поскольку закрытие этого соединения возвращает его в пул. Итак, сначала получите соединение из пула и разверните его только в том месте, где оно вам действительно нужно, а затем, когда вы закончите, закройте исходное соединение, полученное из источника данных.

Не закрывайте развернутое соединение, потому что это закроет фактическое физическое соединение, и это лишает смысла использование пула соединений.

Итак, вкратце:

try (Connection connection = dataSource.getConnection()) {
    OracleConnection unwrapped = connection.unwrap(oracle.jdbc.driver.OracleConnection.class)

    ...

    // Do not close (or use try-with-resources) on unwrapped
}
person Mark Rotteveel    schedule 15.06.2017
comment
Большое спасибо! Теперь это имеет смысл. Я думал, что закрытие развернутого файла освободит ссылки на объединенное в пул соединение, и оно должно быть, по крайней мере, подвергнуто сборке мусора. Теперь вижу где это, патч на подходе :) - person JustinL; 15.06.2017