2015-07-22 5 views
4

Следующий код запускает предупреждение о утечке соединения. Я использую OpenJDK 1.7.0_80 и HikariCP 2.2.5 (также воспроизводимый с последним HikariCP 2.3.9). Я что-то упускаю?Утечка соединения с помощью try-with-resources и HikariCP

import java.sql.Connection; 
import java.sql.PreparedStatement; 
import java.sql.SQLException; 

import org.junit.Test; 

import com.zaxxer.hikari.HikariConfig; 
import com.zaxxer.hikari.HikariDataSource; 

public class HikaryAutoCloseTest { 
    private static HikariDataSource configureDataSource() { 
     try { 
      Class.forName("org.postgresql.Driver"); 
     } catch (ClassNotFoundException e) { 
      throw new RuntimeException(e); 
     } 

     HikariConfig config = new HikariConfig(); 
     config.setJdbcUrl("jdbc:postgresql://127.0.0.1/DATABASE"); 
     config.setUsername("USERNAME"); 
     config.setPassword("PASSWORD"); 

     config.setLeakDetectionThreshold(10000); 

     config.addDataSourceProperty("cachePrepStmts", "true"); 
     config.addDataSourceProperty("useServerPrepStmts", "true"); 

     return new HikariDataSource(config); 
    } 

    @Test 
    public void testHikaryAutoClose() { 
     HikariDataSource dataSource = configureDataSource(); 

     boolean ret = shouldNotLeakConnection(dataSource); 
     if (ret) { 
      System.out.println("UPDATE okey"); 
     } 

     /* Wait for LeakTask to complain */ 
     try { 
      Thread.sleep(20000); 
     } catch (InterruptedException e) { 
      throw new RuntimeException(e); 
     } 
     System.out.println("Exiting"); 
    } 

    private boolean shouldNotLeakConnection(HikariDataSource dataSource) { 
     String sql = "INSERT INTO error_logs (description) values (?)"; 

     try (Connection conn = dataSource.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql);) { 
      stmt.setString(1, "description"); 
      return stmt.executeUpdate() != 0; // minor changes to this line remove the leak 
     } catch (SQLException e) { 
      throw new RuntimeException(e); 
     } 
    } 
} 

UPDATE: Слегка изменив оператор возврата исключает утечку:

private boolean shouldNotLeakConnection(HikariDataSource dataSource) { 
    String sql = "INSERT INTO error_logs (description) values (?)"; 

    try (Connection conn = dataSource.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql);) { 
     stmt.setString(1, "description"); 
     boolean ret = stmt.executeUpdate() != 0; 
     return ret; 
    } catch (SQLException e) { 
     throw new RuntimeException(e); 
    } 
} 
+1

На первый взгляд это выглядит хорошо. Я должен сам запустить этот тест утром, но я скажу, что эти свойства DataSource предназначены для MySQL не PostgreSQL. Что произойдет, если вы закрываете соединение явно? – brettw

+0

@brettw Явным образом вызов 'conn.close()' также решает проблему утечки, но он игнорирует всю цель try-with-resources и компилятор заботится о надлежащем закрытии ресурсов. – user1202136

+1

Я понятия не имею, что происходит с вашей средой, но я запустил приведенный выше код (без изменений) под Eclipse с веткой HikariCP dev и под IntelliJ с HikariCP 2.3.9, и оба выполнялись без регистрации исключения утечки. – brettw

ответ

4

Похоже, проблема пропаренного из-за ошибки в AspectJ 1.7.2. Он производил следующий байт-код:

private boolean shouldNotLeakConnection(com.zaxxer.hikari.HikariDataSource); 
    Code: 
     0: ldc   #115    // String INSERT INTO error_logs (description) values (?) 
     2: astore_2  
     3: aconst_null 
     4: astore_3  
     5: aconst_null 
     6: astore  4 
     8: aload_1  
     9: invokevirtual #117    // Method com/zaxxer/hikari/HikariDataSource.getConnection:()Ljava/sql/Connection; 
     12: astore  5 
     14: aload   5 
     16: aload_2  
     17: invokeinterface #121, 2   // InterfaceMethod java/sql/Connection.prepareStatement:(Ljava/lang/String;)Ljava/sql/PreparedStatement; 
     22: astore  6 
     24: aload   6 
     26: iconst_1  
     27: ldc   #127    // String description 
     29: invokeinterface #129, 3   // InterfaceMethod java/sql/PreparedStatement.setString:(ILjava/lang/String;)V 
     34: aload   6 
     36: invokeinterface #135, 1   // InterfaceMethod java/sql/PreparedStatement.executeUpdate:()I 
     41: ifeq   46 
     44: iconst_1  
     45: ireturn  
     46: iconst_0  
     47: aload   6 
     49: ifnull  59 
     52: aload   6 
     54: invokeinterface #139, 1   // InterfaceMethod java/sql/PreparedStatement.close:()V 
     59: aload   5 
     61: ifnull  71 
     64: aload   5 
     66: invokeinterface #142, 1   // InterfaceMethod java/sql/Connection.close:()V 
     71: ireturn  
     72: astore_3  
     73: aload   6 
     75: ifnull  85 
     78: aload   6 
     80: invokeinterface #139, 1   // InterfaceMethod java/sql/PreparedStatement.close:()V 
     85: aload_3  
     86: athrow   
     87: astore  4 
     89: aload_3  
     90: ifnonnull  99 
     93: aload   4 
     95: astore_3  
     96: goto   111 
     99: aload_3  
    100: aload   4 
    102: if_acmpeq  111 
    105: aload_3  
    106: aload   4 
    108: invokevirtual #143    // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V 
    111: aload   5 
    113: ifnull  123 
    116: aload   5 
    118: invokeinterface #142, 1   // InterfaceMethod java/sql/Connection.close:()V 
    123: aload_3  
    124: athrow   
    125: astore  4 
    127: aload_3  
    128: ifnonnull  137 
    131: aload   4 
    133: astore_3  
    134: goto   149 
    137: aload_3  
    138: aload   4 
    140: if_acmpeq  149 
    143: aload_3  
    144: aload   4 
    146: invokevirtual #143    // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V 
    149: aload_3  
    150: athrow   
    151: astore_3  
    152: new   #25     // class java/lang/RuntimeException 
    155: dup   
    156: aload_3  
    157: invokespecial #27     // Method java/lang/RuntimeException."<init>":(Ljava/lang/Throwable;)V 
    160: athrow   
    Exception table: 
     from to target type 
      24 47 72 any 
      71 72 72 any 
      14 59 87 any 
      71 87 87 any 
      8 125 125 any 
      3 71 151 Class java/sql/SQLException 
      72 151 151 Class java/sql/SQLException 

Обратите внимание на линии 45, ireturn перепрыгивает через два close() методов.

С AspectJ 1.8.6, правильный байт-код генерируется:

private boolean shouldNotLeakConnection(com.zaxxer.hikari.HikariDataSource); 
    Code: 
     0: ldc   #115    // String INSERT INTO error_logs (description) values (?) 
     2: astore_2  
     3: aconst_null 
     4: astore_3  
     5: aconst_null 
     6: astore  4 
     8: aload_1  
     9: invokevirtual #117    // Method com/zaxxer/hikari/HikariDataSource.getConnection:()Ljava/sql/Connection; 
     12: astore  5 
     14: aload   5 
     16: aload_2  
     17: invokeinterface #121, 2   // InterfaceMethod java/sql/Connection.prepareStatement:(Ljava/lang/String;)Ljava/sql/PreparedStatement; 
     22: astore  6 
     24: aload   6 
     26: iconst_1  
     27: ldc   #127    // String description 
     29: invokeinterface #129, 3   // InterfaceMethod java/sql/PreparedStatement.setString:(ILjava/lang/String;)V 
     34: aload   6 
     36: invokeinterface #135, 1   // InterfaceMethod java/sql/PreparedStatement.executeUpdate:()I 
     41: ifeq   48 
     44: iconst_1  
     45: goto   49 
     48: iconst_0  
     49: aload   6 
     51: ifnull  61 
     54: aload   6 
     56: invokeinterface #139, 1   // InterfaceMethod java/sql/PreparedStatement.close:()V 
     61: aload   5 
     63: ifnull  73 
     66: aload   5 
     68: invokeinterface #142, 1   // InterfaceMethod java/sql/Connection.close:()V 
     73: ireturn  
     74: astore_3  
     75: aload   6 
     77: ifnull  87 
     80: aload   6 
     82: invokeinterface #139, 1   // InterfaceMethod java/sql/PreparedStatement.close:()V 
     87: aload_3  
     88: athrow   
     89: astore  4 
     91: aload_3  
     92: ifnonnull  101 
     95: aload   4 
     97: astore_3  
     98: goto   113 
    101: aload_3  
    102: aload   4 
    104: if_acmpeq  113 
    107: aload_3  
    108: aload   4 
    110: invokevirtual #143    // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V 
    113: aload   5 
    115: ifnull  125 
    118: aload   5 
    120: invokeinterface #142, 1   // InterfaceMethod java/sql/Connection.close:()V 
    125: aload_3  
    126: athrow   
    127: astore  4 
    129: aload_3  
    130: ifnonnull  139 
    133: aload   4 
    135: astore_3  
    136: goto   151 
    139: aload_3  
    140: aload   4 
    142: if_acmpeq  151 
    145: aload_3  
    146: aload   4 
    148: invokevirtual #143    // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V 
    151: aload_3  
    152: athrow   
    153: astore_3  
    154: new   #25     // class java/lang/RuntimeException 
    157: dup   
    158: aload_3  
    159: invokespecial #27     // Method java/lang/RuntimeException."<init>":(Ljava/lang/Throwable;)V 
    162: athrow   
    Exception table: 
     from to target type 
      24 49 74 any 
      73 74 74 any 
      14 61 89 any 
      73 89 89 any 
      8 127 127 any 
      3 73 153 Class java/sql/SQLException 
      74 153 153 Class java/sql/SQLException 

Обратите внимание, что в строке 45, ireturn заменяется goto, который не перепрыгнуть двух close() методов.

Спасибо @brettw за то, что помогли мне сузить проблему.

+2

вы очень желанны. – brettw