ormlite-core icon indicating copy to clipboard operation
ormlite-core copied to clipboard

Connections are note released after nested transactions

Open cfeller-init opened this issue 9 months ago • 2 comments

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.

cfeller-init avatar Mar 12 '25 16:03 cfeller-init

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".

j256 avatar Mar 12 '25 18:03 j256

Ok. I've been able to reproduce this. I should have always had the TransactionManager work with a pooled connection source. Duh.

j256 avatar Mar 12 '25 19:03 j256