spring-projects / spring-data-neo4j

Provide support to increase developer productivity in Java when using Neo4j. Uses familiar Spring concepts such as a template classes for core API usage and lightweight repository style data access.
http://spring.io/projects/spring-data-neo4j
Apache License 2.0
832 stars 618 forks source link

Neo4jTransactionManager nullpointer exception while rollback blocks subsequent actions in the same transaction when using ChainedTransactionManager that binds Neo4jTransactionManager and DataSourceTransactionManager [DATAGRAPH-995] #1557

Closed spring-projects-issues closed 4 years ago

spring-projects-issues commented 7 years ago

kostas opened DATAGRAPH-995 and commented

When using a ChainedTransactionManager that binds a DataSourceTransactionManager and Neo4jTransactionManager. A NullPointerException inside Neo4jTransactionManager leads to incorrect behaviour .

Scenario

  1. Start a transaction that writes something in Neo4j, that leads to a unique constaint violation.
  2. Catch the exception and swallow it.
  3. Try to store something in other store. E.g postgres in the same transaction
  4. Nothing is written at step 3, due to the NullpointerException in the Neo4jTransaction manager that tries to rollback a transaction that seems to be null.

A project that reproduces the exact behaviour has been created , Please

please clone and check the project here https://github.com/kostasp/spring-data_neo4j_chained_transaction_problem_demo

public PlatformTransactionManager chainedTransactionManager(SessionFactory sessionFactory) throws Exception {
    Neo4jTransactionManager neoTransactionManager = new Neo4jTransactionManager(sessionFactory);
    DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource());
    return new ChainedTransactionManager(dataSourceTransactionManager, neoTransactionManager);
}
org.springframework.dao.InvalidDataAccessApiUsageException: Transaction is already closed; nested exception is org.neo4j.ogm.exception.TransactionException: Transaction is already closed
        at org.springframework.data.neo4j.transaction.SessionFactoryUtils.convertOgmAccessException(SessionFactoryUtils.java:139) ~[spring-data-neo4j-4.2.1.RELEASE.jar:na]
        at org.springframework.data.neo4j.transaction.Neo4jTransactionManager.doCommit(Neo4jTransactionManager.java:258) ~[spring-data-neo4j-4.2.1.RELEASE.jar:na]
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761) [spring-tx-4.3.8.RELEASE.jar:4.3.8.RELEASE]
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730) [spring-tx-4.3.8.RELEASE.jar:4.3.8.RELEASE]
        at org.springframework.data.transaction.MultiTransactionStatus.commit(MultiTransactionStatus.java:73) [spring-data-commons-1.13.3.RELEASE.jar:na]
        at org.springframework.data.transaction.ChainedTransactionManager.commit(ChainedTransactionManager.java:145) [spring-data-commons-1.13.3.RELEASE.jar:na]
        at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504) [spring-tx-4.3.8.RELEASE.jar:4.3.8.RELEASE]
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292) [spring-tx-4.3.8.RELEASE.jar:4.3.8.RELEASE]
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) [spring-tx-4.3.8.RELEASE.jar:4.3.8.RELEASE]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.3.8.RELEASE.jar:4.3.8.RELEASE]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673) [spring-aop-4.3.8.RELEASE.jar:4.3.8.RELEASE]
        at org.test.neo4j.transaction.service.WrapperService$$EnhancerBySpringCGLIB$$e32c2fad.persistAll(<generated>) [classes/:na]
        at org.test.neo4j.transaction.TestService$TestNodeWrapperObject.run(TestService.java:47) [classes/:na]
        at java.lang.Thread.run(Thread.java:745) [na:1.8.0_66]
Caused by: org.neo4j.ogm.exception.TransactionException: Transaction is already closed
        at org.neo4j.ogm.drivers.bolt.transaction.BoltTransaction.commit(BoltTransaction.java:94) ~[neo4j-ogm-bolt-driver-2.1.2.jar:na]
        at org.springframework.data.neo4j.transaction.Neo4jTransactionManager.doCommit(Neo4jTransactionManager.java:256) ~[spring-data-neo4j-4.2.1.RELEASE.jar:na]
        ... 12 common frames omitted

Exception in thread "Thread-6" org.springframework.transaction.HeuristicCompletionException: Heuristic completion: outcome state is rolled back; nested exception is java.lang.NullPointerException
        at org.springframework.data.transaction.ChainedTransactionManager.commit(ChainedTransactionManager.java:172)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292)
        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 org.test.neo4j.transaction.service.WrapperService$$EnhancerBySpringCGLIB$$e32c2fad.persistAll(<generated>)
        at org.test.neo4j.transaction.TestService$TestNodeWrapperObject.run(TestService.java:47)
        at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
        at org.springframework.data.neo4j.transaction.Neo4jTransactionManager.doRollback(Neo4jTransactionManager.java:275)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.doRollbackOnCommitException(AbstractPlatformTransactionManager.java:900)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:789)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730)
        at org.springframework.data.transaction.MultiTransactionStatus.commit(MultiTransactionStatus.java:73)
        at org.springframework.data.transaction.ChainedTransactionManager.commit(ChainedTransactionManager.java:145)
        ... 8 more

Affects: 4.2.1 (Ingalls SR1), 4.2.3 (Ingalls SR3)

Reference URL: https://github.com/kostasp/spring-data_neo4j_chained_transaction_problem_demo

spring-projects-issues commented 6 years ago

Nicolas Mervaillie commented

When there is a problem executing a query in Neo4j, the database rollbacks whatever happens client side. We can't control this behavior as it's done at the database level.

See https://github.com/neo4j/neo4j/issues/10098

spring-projects-issues commented 4 years ago

Michael Simons commented

The 4.x series is out of support. Please feel free to raise a new ticket, if the issue is still pressing, kostas. Apart from that: Nicolas Mervaillie is correct. OGMs (the underlying object mapper) architecture leads to that behavior.

However a quick check on the 5.1.x, 5.2.x and 5.3.x reveals that we added a check whether we could retrieve the tx object (in DATAGRAPH-974)