Spring Data Neo4j
  1. Spring Data Neo4j
  2. DATAGRAPH-191

ChainedTransactionManager does not return connection to DataSource on failure

    Details

      Description

      Spring configuration:

       
          <bean id="txDbo" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
              <property name="dataSource" ref="dboDataSource"/>
          </bean>
      
          <bean id="txMain" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
              <property name="dataSource" ref="mainDataSource"/>
          </bean>
      
          <bean id="txChained" class="org.springframework.data.neo4j.transaction.ChainedTransactionManager">
              <constructor-arg>
                  <util:list>
                      <ref bean="txDbo"/>
                      <ref bean="txMain"/>
                  </util:list>
              </constructor-arg>
          </bean>
      
          <int-jdbc:inbound-channel-adapter data-source="dboDataSource"
                                            channel="inboundDboListChannel"
                                            update="#{@sql['DBO_SET_TRANSIT']}"
                                            update-per-row="true"
                                            query="#{@sql['DBO_GET_MESSAGES']}"
                                            max-rows-per-poll="1000"
                                            row-mapper="dboRowMapper"
                  >
              <int:poller fixed-delay="500">
                  <int:transactional transaction-manager="txChained"/>
              </int:poller>
          </int-jdbc:inbound-channel-adapter>
      
      

      After DBO connection crash (closed due to network issues) I get an error every time executor uses the crashed thread (other threads perform fine):

      [#2012-01-30 18:09:58,619|ERROR|task-scheduler-3|o.s.i.h.LoggingHandler --: |org.springframework.jdbc.UncategorizedSQLException: StatementCallback; uncategorized SQLException for SQL [
              select MessageID, Number, source_number , Message, Priority, DocID
                  from MessagesToSend order by MessageID 
          ]; SQL state [null]; error code [0]; The connection is closed.; nested exception is com.microsoft.sqlserver.jdbc.SQLServerException: The connection is closed.
      	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:83)
      	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80)
      	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80)
      	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:407)
      	at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:456)
      	at org.springframework.integration.jdbc.JdbcPollingChannelAdapter.doPoll(JdbcPollingChannelAdapter.java:200)
      	at org.springframework.integration.jdbc.JdbcPollingChannelAdapter.poll(JdbcPollingChannelAdapter.java:148)
      	at org.springframework.integration.jdbc.JdbcPollingChannelAdapter.receive(JdbcPollingChannelAdapter.java:135)
      	at org.springframework.integration.endpoint.SourcePollingChannelAdapter.doPoll(SourcePollingChannelAdapter.java:89)
      	at org.springframework.integration.endpoint.AbstractPollingEndpoint$1.call(AbstractPollingEndpoint.java:146)
      	at org.springframework.integration.endpoint.AbstractPollingEndpoint$1.call(AbstractPollingEndpoint.java:144)
      	at sun.reflect.GeneratedMethodAccessor177.invoke(Unknown Source)
      	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      	at java.lang.reflect.Method.invoke(Method.java:597)
      	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
      	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
      	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
      	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
      	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
      	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
      	at $Proxy111.call(Unknown Source)
      	at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller$1.run(AbstractPollingEndpoint.java:207)
      	at org.springframework.integration.util.ErrorHandlingTaskExecutor$1.run(ErrorHandlingTaskExecutor.java:52)
      	at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:48)
      	at org.springframework.integration.util.ErrorHandlingTaskExecutor.execute(ErrorHandlingTaskExecutor.java:49)
      	at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller.run(AbstractPollingEndpoint.java:202)
      	at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:53)
      	at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
      	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
      	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
      	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
      	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:98)
      	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:206)
      	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
      	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
      	at java.lang.Thread.run(Thread.java:662)
      Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: The connection is closed.
      	at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDriverError(SQLServerException.java:171)
      	at com.microsoft.sqlserver.jdbc.SQLServerConnection.checkClosed(SQLServerConnection.java:319)
      	at com.microsoft.sqlserver.jdbc.SQLServerConnection.createStatement(SQLServerConnection.java:1829)
      	at com.microsoft.sqlserver.jdbc.SQLServerConnection.createStatement(SQLServerConnection.java:1542)
      	at org.apache.tomcat.dbcp.dbcp.DelegatingConnection.createStatement(DelegatingConnection.java:257)
      	at org.apache.tomcat.dbcp.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.createStatement(PoolingDataSource.java:216)
      	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:390)
      	... 32 more
      #] 
      
      1. chained.log
        82 kB
        Andrei Tuzhilin
      2. ChainedTransactionManager.java
        4 kB
        Andrei Tuzhilin
      3. fix_mts_init.patch
        3 kB
        Andrei Tuzhilin

        Activity

        Hide
        Giovanni Botta added a comment - - edited

        It's actually still there in 2.2.0. Was this bug ever fixed?

        Show
        Giovanni Botta added a comment - - edited It's actually still there in 2.2.0. Was this bug ever fixed?
        Hide
        Andrei Tuzhilin added a comment - - edited

        I've missed new org.springframework.data:spring-data-neo4j-tx artifact back then...

        Yes, the problem is still there, it resides primarily in org.springframework.data.neo4j.transaction.ChainedTransactionManager.getTransaction. The block of code has no exception handling, so when any error occurs during transaction creation, the already created transactions does not rollback (thus leaving connections abandoned).

        ChainedTransactionManager.java
                for (PlatformTransactionManager transactionManager : transactionManagers) {
                    mts.registerTransactionManager(definition, transactionManager);
                }
        

        I use my own workaround (ChainedTransactionManager.java), maybe it will come useful.

        ChainedTransactionManager.java
        //...
        try {
            for (PlatformTransactionManager transactionManager : transactionManagers) {
                mts.registerTransactionManager(definition, transactionManager);
            }
        } catch (Exception ex) {
            transactionStatuses = (Map<PlatformTransactionManager, TransactionStatus>)
                        ReflectionUtils.getField(transactionStatusesField, mts);
            for (PlatformTransactionManager transactionManager : transactionManagers) {
                try {
                    if (transactionStatuses.get(transactionManager) != null)
                        transactionManager.rollback(transactionStatuses.get(transactionManager));
                } catch (Exception ex2) {
                    logger.warn("Rollback exception (" + transactionManager + ") " + ex2.getMessage(), ex2);
                }
            }
        
            synchronizationManager.clearSynchronization();
        
            throw new CannotCreateTransactionException(ex.getMessage(), ex);
        }
        //...
        
        Show
        Andrei Tuzhilin added a comment - - edited I've missed new org.springframework.data:spring-data-neo4j-tx artifact back then... Yes, the problem is still there, it resides primarily in org.springframework.data.neo4j.transaction.ChainedTransactionManager.getTransaction . The block of code has no exception handling, so when any error occurs during transaction creation, the already created transactions does not rollback (thus leaving connections abandoned). ChainedTransactionManager.java for (PlatformTransactionManager transactionManager : transactionManagers) { mts.registerTransactionManager(definition, transactionManager); } I use my own workaround ( ChainedTransactionManager.java ), maybe it will come useful. ChainedTransactionManager.java //... try { for (PlatformTransactionManager transactionManager : transactionManagers) { mts.registerTransactionManager(definition, transactionManager); } } catch (Exception ex) { transactionStatuses = (Map<PlatformTransactionManager, TransactionStatus>) ReflectionUtils.getField(transactionStatusesField, mts); for (PlatformTransactionManager transactionManager : transactionManagers) { try { if (transactionStatuses.get(transactionManager) != null ) transactionManager.rollback(transactionStatuses.get(transactionManager)); } catch (Exception ex2) { logger.warn( "Rollback exception (" + transactionManager + ") " + ex2.getMessage(), ex2); } } synchronizationManager.clearSynchronization(); throw new CannotCreateTransactionException(ex.getMessage(), ex); } //...
        Hide
        Giovanni Botta added a comment -

        Can this be considered for a patch?

        Show
        Giovanni Botta added a comment - Can this be considered for a patch?
        Hide
        Andrei Tuzhilin added a comment -

        I've attached the patch: fix_mts_init.patch

        Show
        Andrei Tuzhilin added a comment - I've attached the patch: fix_mts_init.patch
        Hide
        Oliver Gierke added a comment -

        Fixed in master and bugfix branch by applying the patch. Note that for the next major release the ChainedTransactionManager will be moved into Spring Data Commons (see DATACMNS-310 for details).

        Show
        Oliver Gierke added a comment - Fixed in master and bugfix branch by applying the patch. Note that for the next major release the ChainedTransactionManager will be moved into Spring Data Commons (see DATACMNS-310 for details).

          People

          • Assignee:
            Oliver Gierke
            Reporter:
            Andrei Tuzhilin
          • Votes:
            1 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: