La mejor manera de tackle connection leaks is to do it during testing.
Puede utilizar una utilidad automatizada para que cada prueba verifique si hay una fuga de conexión.
@BeforeClass
public static void initConnectionLeakUtility() {
if (enableConnectionLeakDetection) {
connectionLeakUtil = new ConnectionLeakUtil();
}
}
@AfterClass
public static void assertNoLeaks() {
if (enableConnectionLeakDetection) {
connectionLeakUtil.assertNoLeaks();
}
}
El ConnectionLeakUtil
se parece a esto:
public class ConnectionLeakUtil {
private JdbcProperties jdbcProperties = JdbcProperties.INSTANCE;
private List idleConnectionCounters =
Arrays.asList(
H2IdleConnectionCounter.INSTANCE,
OracleIdleConnectionCounter.INSTANCE,
PostgreSQLIdleConnectionCounter.INSTANCE,
MySQLIdleConnectionCounter.INSTANCE
);
private IdleConnectionCounter connectionCounter;
private int connectionLeakCount;
public ConnectionLeakUtil() {
for (IdleConnectionCounter connectionCounter :
idleConnectionCounters) {
if (connectionCounter.appliesTo(
Dialect.getDialect().getClass())) {
this.connectionCounter = connectionCounter;
break;
}
}
if (connectionCounter != null) {
connectionLeakCount = countConnectionLeaks();
}
}
public void assertNoLeaks() {
if (connectionCounter != null) {
int currentConnectionLeakCount = countConnectionLeaks();
int diff = currentConnectionLeakCount - connectionLeakCount;
if (diff > 0) {
throw new ConnectionLeakException(
String.format(
"%d connection(s) have been leaked! Previous leak count: %d, Current leak count: %d",
diff,
connectionLeakCount,
currentConnectionLeakCount
)
);
}
}
}
private int countConnectionLeaks() {
try (Connection connection = newConnection()) {
return connectionCounter.count(connection);
}
catch (SQLException e) {
throw new IllegalStateException(e);
}
}
private Connection newConnection() {
try {
return DriverManager.getConnection(
jdbcProperties.getUrl(),
jdbcProperties.getUser(),
jdbcProperties.getPassword()
);
}
catch (SQLException e) {
throw new IllegalStateException(e);
}
}
}
Los IdleConnectionCounter
implementaciones se pueden encontrar en este blog post, y la versión de MySQL así:
public class MySQLIdleConnectionCounter implements IdleConnectionCounter {
public static final IdleConnectionCounter INSTANCE =
new MySQLIdleConnectionCounter();
@Override
public boolean appliesTo(Class<? extends Dialect> dialect) {
return MySQL5Dialect.class.isAssignableFrom(dialect);
}
@Override
public int count(Connection connection) {
try (Statement statement = connection.createStatement()) {
try (ResultSet resultSet = statement.executeQuery(
"SHOW PROCESSLIST")) {
int count = 0;
while (resultSet.next()) {
String state = resultSet.getString("command");
if ("sleep".equalsIgnoreCase(state)) {
count++;
}
}
return count;
}
}
catch (SQLException e) {
throw new IllegalStateException(e);
}
}
}
Ahora, al ejecutar su pruebas, obtendrá una falla cuando se está filtrando una conexión.
¿Cómo se compara esto con p6spy? –
@ Thorbjørn Lo considero una alternativa más moderna (la última versión de P6Spy fue hace más de 7 años): usa SLF4J, ofrece algunas características y opciones de configuración que me gustan, es compatible con JDBC 4 ... Lo estoy usando como reemplazo ahora. –
ConnLeakFinder funcionó muy bien para mí. Gracias por el enlace. –