A ConnectionLeakException arised when I run test

A ConnectionLeakException arised when I run test with git tag 5.6.9.
No exception will be arised on the latest main branch. But I think there is no connection leak detection on the latest main branch.

My test commands:

git clone -b 5.6.9 --depth 1 https://github.com/hibernate/hibernate-orm.git
cd hibernate-orm
./docker_db.sh postgresql_9_5
cd documentation
HIBERNATE_CONNECTION_LEAK_DETECTION=true ../gradlew test -Pdb=pgsql --tests "org.hibernate.userguide.batch.BatchTest"

The error message:

org.hibernate.testing.junit4.CallbackException: org.hibernate.testing.junit4.BaseUnitTestCase#assertNoLeaks
	at org.hibernate.testing.junit4.TestClassMetadata.performCallbackInvocation(TestClassMetadata.java:208)
	at org.hibernate.testing.junit4.TestClassMetadata.invokeCallback(TestClassMetadata.java:192)
	at org.hibernate.testing.junit4.TestClassMetadata.performCallbacks(TestClassMetadata.java:184)
	at org.hibernate.testing.junit4.TestClassMetadata.performAfterClassCallbacks(TestClassMetadata.java:216)
	at org.hibernate.testing.junit4.AfterClassCallbackHandler.evaluate(AfterClassCallbackHandler.java:26)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
	at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
	at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:119)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
	at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
	at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:414)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: org.hibernate.testing.jdbc.leak.ConnectionLeakException: 1 connection(s) have been leaked! Previous leak count: 0, Current leak count: 1
	at org.hibernate.testing.jdbc.leak.ConnectionLeakUtil.assertNoLeaks(ConnectionLeakUtil.java:54)
	at org.hibernate.testing.junit4.BaseUnitTestCase.assertNoLeaks(BaseUnitTestCase.java:63)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.hibernate.testing.junit4.TestClassMetadata.performCallbackInvocation(TestClassMetadata.java:205)
	... 36 more

No exception will be arised on the latest main branch. But I find that there is no connection leak detection at all.

// hibernate-testing/src/main/java/org/hibernate/testing/jdbc/leak/ConnectionLeakUtil.java
for ( IdleConnectionCounter connectionCounter : idleConnectionCounters ) {
	if ( connectionCounter.appliesTo( DialectContext.getDialect().getClass() ) ) {
		this.connectionCounter = connectionCounter;
		break;
	}
}

On the main branch, after running the above code, ConnectionLeakUtil.connectionCounter is still null. So assertNoLeaks is unused.

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
		) );
	}
}
}

it seems the connection leak is due to the use org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl (A special connection provider that is shared across test runs for better performance ) for test does not close the connection when the stop method is called.

@Override
	public void stop() {
		// No need to stop as this is a shared instance
		validateConnectionsReturned();
	}

For the main branch the ConnectionLeakUtil#connectioCounter is not set because the implementation of IdleConnectionCounter#connectionCounter.appliesTo(..) has to be changed (Iā€™m working on this).

1 Like