Spring Data Neo4j: TransactionException: Transaction is already closed when using multiple profiles in integration test

415 Views Asked by At

I have a working set of integration tests using Spring Data Neo4j 4.x (with an embedded database). In order to activate this, I have defined a profile called integrationTest which I activate in related tests via @ActiveProfiles("integrationTest") (though this is extracted into a meta-annotation).

All integration-tests reside in a separate source set (integrationTest) and are run separate in the build. They are not mixed with regular tests. Inside that source set, all tests use only the integrationTest profile up to now.

I now have new requirement: Some of the tests should use another implementation of a service than the default. For that I defined another profile (lets call it otherProfile) which is activated in addition to the integrationTest profile (i.e. @ActiveProfiles({"integrationTest", "otherProfile"}).

When running single tests, that works fine. When running multiple/all tests in succession though, this starts to fail with the following exception:

2017-11-16 18:32:27,042 W [main ] (EmbeddedTransaction.java:64) Transaction is already closed 2017-11-16 18:32:27,043 E [main ] (TransactionAspectSupport.java:530) Application exception overridden by rollback exception java.lang.RuntimeException: org.neo4j.ogm.exception.TransactionException: Transaction is already closed at org.neo4j.ogm.drivers.embedded.request.EmbeddedRequest.executeRequest(EmbeddedRequest.java:180) at org.neo4j.ogm.drivers.embedded.request.EmbeddedRequest.execute(EmbeddedRequest.java:74) at org.neo4j.ogm.session.delegates.DeleteDelegate.purgeDatabase(DeleteDelegate.java:265) at org.neo4j.ogm.session.Neo4jSession.purgeDatabase(Neo4jSession.java:417) at sun.reflect.GeneratedMethodAccessor228.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.data.neo4j.transaction.SharedSessionCreator$SharedSessionInvocationHandler.invoke(SharedSessionCreator.java:131) at com.sun.proxy.$Proxy143.purgeDatabase(Unknown Source) at de.moneysoft.core.util.testUtil.GraphDatabaseCleanerService.cleanUp(GraphDatabaseCleanerService.java:53) at de.moneysoft.core.util.testUtil.GraphDatabaseCleanerService$$FastClassBySpringCGLIB$$a273583c.invoke() at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673) at de.moneysoft.core.util.testUtil.GraphDatabaseCleanerService$$EnhancerBySpringCGLIB$$a1e0845a.cleanUp() at de.moneysoft.core.util.testUtil.GraphDatabaseCleanerRule.cleanUp(GraphDatabaseCleanerRule.java:42) at de.moneysoft.core.util.testUtil.GraphDatabaseCleanerRule.after(GraphDatabaseCleanerRule.java:37) at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:50) at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:48) at org.junit.rules.RunRules.evaluate(RunRules.java:20) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:27) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) Caused by: org.neo4j.ogm.exception.TransactionException: Transaction is already closed at org.neo4j.ogm.drivers.embedded.request.EmbeddedRequest.executeRequest(EmbeddedRequest.java:166) ... 50 common frames omitted

This occurs both when running from IDE as well as when running from gradle. The GraphDatabaseCleanerRule itself doesn't seem to be at fault: In other cases its simply the first interaction with neo4j that fails.

After some debugging I found that it is always the first test after switching profiles that fails. I know that Spring uses the active profiles (amongst other things) in order to cache the application context inbetween tests. I can see that the first test that uses the second profile causes a (partial?) restart of the application context.

I guess that the error comes from having a second connection to the same embedded neo4j instance as the one from the previous context is not closed. I am not aware of any hook that exists to shutdown the connection when profiles are switched (the service in question was not destroyed, so @PreDestroy doesn't help. For this exception, not much explanation can be found. I added debug logging for transactions, but they seem to indicate that no old transaction is reused...

I tried adding @DirtiesContext (both before- and after class mode) without success. I am not sure if this is an (spring data) neo4j problem or just a spring problem. Any suggestions on how to fix or work around this?

Edit: I was using Spring Data Neo4j 4.2.4.RELEASE and now updated to 4.2.8.RELEASE but the problem persists. To clarify: All tests run fine when run alone. Only running tests with the one profile or only running tests with the other profile also works fine. Its only when tests with both profiles are executed, that the error occurs.

Edit 2: For completeness, this is how the mentioned meta-annotations look that activate the different profiles:

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@ActiveProfiles({"integrationTest"})
@SpringBootTest(properties = "some.property=2000")
@ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = TestConfiguration.class)
public @interface Neo4jIntegrationTest
{

}

and

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@ActiveProfiles({"integrationTest", "otherProfile"})
@SpringBootTest(properties = "some.property=2000")
@ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = TestConfiguration.class)
public @interface OtherNeo4jIntegrationTest
{
}
0

There are 0 best solutions below