回滚失败:Spring-batch在数据库重启后连接关闭

3

我正在通过手动杀死和重启我的PostGreSQL数据库来复现连接问题,这是在Spring Batch期间使用的DriverManagerDataSource崩溃了,所以我不得不更改。现在我正在尝试使用带有Hibernate的C3P0。

这篇文章很长,但主要是配置文件或堆栈跟踪,您不一定需要阅读。

我的主要问题是(我认为):c3p0关闭连接,然后Sping-JTA试图回滚它,失败了,导致批处理失败。我不知道如何防止这种情况。


配置:

我正在使用Spring、Spring batch(4.2.3)、hibernate(4.3.6)与c3p0(0.9.5.2)和postgresql。

applicationContext:

<bean id="jpaVendorAdapter"
    class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    <property name="database" value="POSTGRESQL" />
    <property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" />
    <property name="generateDdl" value="false" />
    <property name="showSql" value="false" />
</bean>

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
    <property name="persistenceUnitName" value="ASO_PU" />
    <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

persitence.xml

<persistence-unit name="ASO_PU" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class> ** my entities </class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
        <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>

        <property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
        <property name="hibernate.connection.username" value="postgres" />
        <property name="hibernate.connection.password" value="postgres" />
        <property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5434/aso_test" />
        <property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"/>

        <property name="hibernate.c3p0.maxSize" value="6"/> <!-- maxPoolSize -->
        <property name="hibernate.c3p0.minSize" value="2"/> <!-- minPoolSize -->
        <property name="hibernate.c3p0.maxStatements" value="30"/> <!-- maxStatements -->
        <property name="hibernate.c3p0.validate" value="true"/> <!-- testConnectionOnCheckout -->
        <property name="hibernate.c3p0.preferredTestQuery" value="SELECT 1"/> <!--  -->

            <property name="hibernate.c3p0.acquireRetryAttempts" value="121"/>
            <property name="hibernate.c3p0.acquireRetryDelay" value="2002"/>

        </properties>
    </persistence-unit>

发生了什么:

当应用程序在我终止后尝试连接到数据库时,它会发出警告,并等待数据库再次启动(我认为以下跟踪信息可能不相关,但还是提供一下):

ATTENTION: [c3p0] A PooledConnection that has already signalled a Connection error is still in use!
31 août 2016 11:44:54 com.mchange.v2.c3p0.impl.NewPooledConnection handleThrowable
ATTENTION: [c3p0] Another error has occurred [ org.postgresql.util.PSQLException: This connection has been closed. ] which will not be reported to listeners!
org.postgresql.util.PSQLException: This connection has been closed.
    at org.postgresql.jdbc2.AbstractJdbc2Connection.checkClosed(AbstractJdbc2Connection.java:837)
    at org.postgresql.jdbc2.AbstractJdbc2Connection.rollback(AbstractJdbc2Connection.java:854)
    at com.mchange.v2.c3p0.impl.NewProxyConnection.rollback(NewProxyConnection.java:860)
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doRollback(JdbcTransaction.java:163)
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.rollback(AbstractTransactionImpl.java:211)
    at org.hibernate.jpa.internal.TransactionImpl.rollback(TransactionImpl.java:108)
    at org.springframework.orm.jpa.JpaTransactionManager.doRollback(JpaTransactionManager.java:544)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:853)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager.java:830)
    at org.springframework.transaction.support.TransactionTemplate.rollbackOnException(TransactionTemplate.java:164)
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:137)
    at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:271)
    at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:77)
    at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:368)
    at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215)
    at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:144)
    at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:257)
    at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:198)
    at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148)
    at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:64)
    at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:67)
    at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:165)
    at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:144)
    at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:134)
    at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:304)
    at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:135)
    at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
    at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:128)
    at fr.aso.batch.test.MyBatchTest.test(MyBatchTest.java:100)
    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.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    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:254)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
    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:193)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
31 août 2016 11:44:54 com.mchange.v2.c3p0.impl.NewPooledConnection handleThrowable
ATTENTION: [c3p0] A PooledConnection that has already signalled a Connection error is still in use!

在这个异常之后,批处理就会停止。一旦数据库再次启动(1分钟后),整个批处理就会崩溃:

     01/09/2016 09:22:47.537 [ERROR]    REI - org.springframework.batch.core.job.AbstractJob            Exception encountered in afterStep callback 
org.springframework.transaction.TransactionSystemException: Could not roll back JPA transaction; nested exception is javax.persistence.PersistenceException: unexpected error when rollbacking
    at org.springframework.orm.jpa.JpaTransactionManager.doRollback(JpaTransactionManager.java:548) ~[spring-orm-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:853) ~[spring-tx-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager.java:830) ~[spring-tx-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.completeTransactionAfterThrowing(TransactionAspectSupport.java:503) ~[spring-tx-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:285) ~[spring-tx-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at $Proxy204.afterJob(Unknown Source) ~[na:na]
    at org.springframework.batch.core.listener.CompositeJobExecutionListener.afterJob(CompositeJobExecutionListener.java:60) ~[spring-batch-core-3.0.4.RELEASE.jar:3.0.4.RELEASE]
    at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:346) ~[spring-batch-core-3.0.4.RELEASE.jar:3.0.4.RELEASE]
    at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:135) [spring-batch-core-3.0.4.RELEASE.jar:3.0.4.RELEASE]
    at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) [spring-core-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:128) [spring-batch-core-3.0.4.RELEASE.jar:3.0.4.RELEASE]
    at fr.aso.batch.OpsTest.testRecuperationEtabsEmployantSalariesCNAMJob(OpsTest.java:100) [test-classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0_30]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[na:1.6.0_30]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.6.0_30]
    at java.lang.reflect.Method.invoke(Method.java:597) ~[na:1.6.0_30]
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) [junit-4.12.jar:4.12]
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12]
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) [junit-4.12.jar:4.12]
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) [spring-test-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) [spring-test-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) [spring-test-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:254) [spring-test-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89) [spring-test-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) [spring-test-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:193) [spring-test-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) [.cp/:na]
Caused by: javax.persistence.PersistenceException: unexpected error when rollbacking
    at org.hibernate.jpa.internal.TransactionImpl.rollback(TransactionImpl.java:111) ~[hibernate-entitymanager-4.3.5.Final.jar:4.3.5.Final]
    at org.springframework.orm.jpa.JpaTransactionManager.doRollback(JpaTransactionManager.java:544) ~[spring-orm-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    ... 43 common frames omitted
Caused by: org.hibernate.TransactionException: rollback failed
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.rollback(AbstractTransactionImpl.java:217) ~[hibernate-core-4.3.6.Final.jar:4.3.6.Final]
    at org.hibernate.jpa.internal.TransactionImpl.rollback(TransactionImpl.java:108) ~[hibernate-entitymanager-4.3.5.Final.jar:4.3.5.Final]
    ... 44 common frames omitted
Caused by: org.hibernate.TransactionException: unable to rollback against JDBC connection
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doRollback(JdbcTransaction.java:167) ~[hibernate-core-4.3.6.Final.jar:4.3.6.Final]
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.rollback(AbstractTransactionImpl.java:211) ~[hibernate-core-4.3.6.Final.jar:4.3.6.Final]
    ... 45 common frames omitted
Caused by: org.postgresql.util.PSQLException: This connection has been closed.
    at org.postgresql.jdbc2.AbstractJdbc2Connection.checkClosed(AbstractJdbc2Connection.java:837) ~[postgresql-9.3-1101.jdbc4.jar:na]
    at org.postgresql.jdbc2.AbstractJdbc2Connection.rollback(AbstractJdbc2Connection.java:854) ~[postgresql-9.3-1101.jdbc4.jar:na]
    at com.mchange.v2.c3p0.impl.NewProxyConnection.rollback(NewProxyConnection.java:860) ~[c3p0-0.9.2.1.jar:0.9.2.1]
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doRollback(JdbcTransaction.java:163) ~[hibernate-core-4.3.6.Final.jar:4.3.6.Final]
    ... 46 common frames omitted

从这个堆栈跟踪来看,我认为情况是这样的:当数据库宕机时,c3p0关闭了连接,在JTA尝试回滚时,c3p0挂起直到数据库重新启动。一旦它再次启动,回滚就可以开始了,但它试图在已关闭的连接上回滚,因此导致了杀死整个批处理的异常。
我以为当连接关闭时,c3p0会得到一个新的连接,但我想这在回滚时并不真正可能(它会在之后尝试吗?),Spring JTA必须在尝试重新连接之前尝试回滚。
问题是:首先,我不知道我说的是不是正确的... JTA是否会在关闭的事务上尝试回滚?
如果是这样,我没有找到任何关于如何处理JTA回滚期间出现的问题的信息,或者可能是c3p0?hibernate?还是甚至是Spring批处理?
我能做什么?(无论是确定问题所在还是如何解决它)。
非常感谢您的帮助。提前致谢(这是相当长的帖子...)
注:崩溃随时都可能发生,在这里它发生在处理器上,因为我关闭了数据库。但是它也可能发生在Spring批处理更新其job_instance_execution表或任何其他时间,但我认为这一切都由Spring-JTA/hibernate/c3p0进行管理,因此我正在关注这个而不是Spring批处理。
1个回答

2
除非您使用正式的分布式事务(即XAConnection),否则 JDBC 事务强制执行的级别是 Connection 对象。如果连接在未提交事务工作的情况下中断,那么该工作将会丢失。没有什么可以做的。如果您有部分工作希望在连接中断时提交,请以更细粒度的方式定义事务,以便您希望提交的工作立即得到提交。
除非您设置了 c3p0.unreturnedConnectionTimeout(不是您的问题),否则 c3p0 永远不会从应用程序中删除 Connections。c3p0正在报告来自 Postgres 的错误,Postgres已经注意到底层连接已关闭。在应用程序(直接或通过 hibernate)调用连接上的 close() 之前,无论它有多糟糕,都会检出它。
在您所描述的一切中,唯一不寻常的事情是第二个投诉之前的暂停。可能的序列是:
1. 连接已关闭 2. 应用程序尝试使用它时发生异常 3. 应用程序尝试回滚时发生进一步异常(您显示的第一个堆栈跟踪) 4. 希望 JPA/hibernate/任何东西仔细调用 close() 将连接返回到连接池的控制 5. 在继续之前,JPA/hibernate 等待新的、有效的连接可用。 6. 然后使用异常向您的应用程序报告之前的故障。
步骤 5 和 6 的顺序有点令人惊讶。从概念上讲,JPA 没有理由不能立即向应用程序报告故障。除此以外,一切都正常。
“我能做什么?”
确保编写应用程序时了解 JPA 失败是可能的,并确保应用程序始终处于一致的状态。在失败时,您的应用程序可以重试失败的工作,也可以简单地向客户端(无论是通过某种消息通知终端用户还是通过异常向其他软件)报告失败。如果即使重试后故障仍然存在,最终可能必须报告故障(除非有某种方法可以解决和恢复故障而不需要数据库,这是不太可能的)。没有什么应用程序可以做来确保网络上的数据库不会消失。

在c3p0级别,如果您希望的话,可以设置testConnectionOnCheckinidleConnectionTestPeriod,以确保在数据库掉线频繁时及时清除不良连接。(使用hibernate.c3p0.validate通过testConnectionOnCheckout的当前设置在正确性方面是好的,但它会导致池惰性地替换连接,因为客户端需要它们)。有关连接测试文档,请参见此处此处


非常感谢,我会检查! - Asoub

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接