Hi Francesco, If you want, you can send the patch directly to me, and we'll have a look at it this week.
Thanks, David On Sat, Aug 14, 2010 at 1:49 PM, Degrassi Francesco < [email protected]> wrote: > > Hello everybody, > > We're evaluating Neo4j for integration in two products we are developing. > > One of the aspects we are interested in is making an embedded neo4j > instance > participate in JTA/XA transactions with a postgresql datasource and > possibly > JMS. > > > > I'm sorry if this is a bit long but the matter is complex. > > > > From what i understood after a bit of research, currently neo4j does not > allow > to use an external TransactionManager out of the box. > > I then dug into the source code and simply patched the following classes to > allow me to pass an external JTA TransactionManager instance down to the > TxModule, replacing Neo4j own TxManager. > > > > For our tests we used the following: > > * Neo4J 1.1 > > * Atomikos TransactionEssentials JTA Manager 3.6.5 > > * postgresql XA-enabled JDBC driver wrapped in an AtomikosDataSourceBean > > > > We created a simple test app which creates a very minimal Spring > ApplicationContext which sets up the neo4j database, the JTA manager, the > JDBC > datasource and a sample transactional service which interacts with both the > relational database and the graph database; we used Spring > "tx:annotation-driven" infrastructure to handle begin/commit/rollback of > the > global transactions. > > > > Upon starting it, we got the following exception: > > > > org.neo4j.kernel.impl.transaction.LockNotFoundException: No transaction > lock > element found for Placebo tx for thread > > Thread[net.emaze.springexperiment.App.main(),5,net.emaze.springexperiment.App] > > at > org.neo4j.kernel.impl.transaction.RWLock.releaseWriteLock(RWLock.java:345) > > at > > org.neo4j.kernel.impl.transaction.LockManager.releaseWriteLock(LockManager.java:202) > > at > org.neo4j.kernel.impl.core.LockReleaser.releaseLocks(LockReleaser.java:337) > > at > > org.neo4j.kernel.impl.core.LockReleaser$ReadOnlyTxReleaser.afterCompletion(LockReleaser.java:713) > > at com.atomikos.icatch.jta.Sync2Sync.afterCompletion(Sync2Sync.java:91) > > at com.atomikos.icatch.imp.SynchToFSM.doAfterCompletion(SynchToFSM.java:38) > > at com.atomikos.icatch.imp.SynchToFSM.entered(SynchToFSM.java:59) > > at com.atomikos.finitestates.FSMImp.notifyListeners(FSMImp.java:197) > > at com.atomikos.finitestates.FSMImp.setState(FSMImp.java:288) > > at com.atomikos.icatch.imp.CoordinatorImp.setState(CoordinatorImp.java:498) > > at > > com.atomikos.icatch.imp.CoordinatorImp.setStateHandler(CoordinatorImp.java:328) > > at > > com.atomikos.icatch.imp.CoordinatorStateHandler.commit(CoordinatorStateHandler.java:730) > > at > > com.atomikos.icatch.imp.IndoubtStateHandler.commit(IndoubtStateHandler.java:225) > > at com.atomikos.icatch.imp.CoordinatorImp.commit(CoordinatorImp.java:828) > > at > com.atomikos.icatch.imp.CoordinatorImp.terminate(CoordinatorImp.java:1127) > > at > > com.atomikos.icatch.imp.CompositeTerminatorImp.commit(CompositeTerminatorImp.java:151) > > at com.atomikos.icatch.jta.TransactionImp.commit(TransactionImp.java:298) > > at > > com.atomikos.icatch.jta.TransactionManagerImp.commit(TransactionManagerImp.java:612) > > at > > com.atomikos.icatch.jta.UserTransactionImp.commit(UserTransactionImp.java:168) > > at > > org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1028) > > at > > org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:732) > > at > > org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:701) > > at > > org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:321) > > at > > org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:116) > > at > > org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171) > > at > > org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) > > at $Proxy9.success(Unknown Source) > > at net.emaze.springexperiment.App.main(App.java:21) > > at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) > > at > sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) > > at > > sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) > > at java.lang.reflect.Method.invoke(Method.java:597) > > at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:291) > > at java.lang.Thread.run(Thread.java:619) > > > > Further analysis resulted in the following hypothesis. > > 1. the external JTA TransactionManager correctly completes the transaction > > 2. the TransactionManager then calls the Synchronization.afterCompletion() > method on the LockReleaser$ReadOnlyTxReleaser for the (now finished) > transaction. > > 3. This causes LockReleaser.releaseLocks() to be called, which through > various > calls invokes the RWLock.releaseWriteLock() method. > > 4. At this point RWLock.releaseWriteLock() calls > RagManager.getCurrentTransaction(), which delegates to the > TransactionManager, > which correctly returns null, since the transaction is already completed. > > 5. RWLock.releaseWriteLock then creates a PlaceboTransaction and looks up > the > txLockElementMap for the TxLockElement associated to the > PlaceboTransaction. > Since the TxLockElement it is looking for was previously associated to the > actual global transaction which was just completed, this fails and the > LockNotFoundException is thrown. > > > > The problem, IMHO, is caused by the fact that, in the execution flow of an > afterCompletion() callback, the TransactionManager is requested for the > current > global transaction, which does not exist at this point. > > To fix it, i hacked the following classes to allow the > LockReleaser$ReadOnlyTxReleaser to pass the transaction reference stored at > creation time down to the RWLock.releaseWriteLock() method, which now can > look > up the correct TxLockElement in the txLockElementMap using the correct key. > > > > Changed classes: > > org.neo4j.kernel.core.LockReleaser > > org.neo4j.kernel.impl.transaction.LockManager > > org.neo4j.kernel.impl.transaction.RWLock > > > > At this point, my doubts are the following: > > * I thought that injecting an external TransactionManager for neo4j to use > would be easier; is my approach valid in this regard? Is there a specific > reason > why the TransactionManager implementation is not easily pluggable ? > > * Regarding the potential bug i found after injecting an external > TransactionManager, is my analysis correct ? Can it, or some other > solution, be > integrated in a future release ? > > > > Handling this scenario is pretty important for us in choosing if we should > proceed in using neo4j in our app. > > I can send you the two patches i made fron neo4j-kernel trunk and the > sample > app, if needed; i did not know if it was ok to attach it to the mailing > list > post. > > > > Thanks for your time. > > > > Francesco Degrassi > _______________________________________________ > Neo4j mailing list > [email protected] > https://lists.neo4j.org/mailman/listinfo/user > _______________________________________________ Neo4j mailing list [email protected] https://lists.neo4j.org/mailman/listinfo/user

