Connections are note released after nested transactions
Hello,
I think there is a connection leak if you use nested transactions with a connection pool like Hikari. Here is a small example:
private final static String DATABASE_URL = "jdbc:h2:mem:account";
public static void main(String[] args) throws Exception {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setJdbcUrl(DATABASE_URL);
HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);
try (ConnectionSource connectionSource = new DataSourceConnectionSource(hikariDataSource, DATABASE_URL)) {
TransactionManager.callInTransaction(connectionSource, (Callable<Void>) () -> {
System.out.println("outer start");
TransactionManager.callInTransaction(connectionSource, (Callable<Void>) () -> {
System.out.println("inner");
return null;
});
System.out.println("outer end");
return null;
});
System.out.format("Active connections : %d\n", hikariDataSource.getHikariPoolMXBean().getActiveConnections());
}
}
At the end, I think, that active connections should be 0, but they are 1.
I think this is connected to this method in the TransactionManager:
public static <T> T callInTransaction(String tableName, final ConnectionSource connectionSource,
final Callable<T> callable) throws SQLException {
DatabaseConnection connection = connectionSource.getReadWriteConnection(tableName);
boolean saved = false;
try {
saved = connectionSource.saveSpecialConnection(connection);
return callInTransaction(connection, saved, connectionSource.getDatabaseType(), callable);
} finally {
// we should clear aggressively
if (saved) {
connectionSource.clearSpecialConnection(connection);
}
connectionSource.releaseConnection(connection);
}
}
If we are in a nested context then saved is false (as we cannot save if we already have a special connection). saveSpecialConnection still increases the internal count in the NestedConnection in BaseConnectionSource. But we do not call clearSpecialConnection to lower that count.
Yeah I see it. Hrm. Not sure what the right fix is. I think the bug is in the TransactionManager. Do you want to submit a fix which always calls clearSpecialConnection() even if saved == false?
You'll also need to fix the TransactionManagerTest testNestedTransactionsReleaseFails() test which specifically says incorrectly that "// should only get one of these because we only returned save once".
Ok. I've been able to reproduce this. I should have always had the TransactionManager work with a pooled connection source. Duh.