【问题标题】:Guice JPA - "This connection has been closed." errorGuice JPA - “此连接已关闭。”错误
【发布时间】:2012-10-16 13:08:22
【问题描述】:

在 DB 断开空闲连接或 DB 关闭并备份后,我在 web 应用中收到以下错误:

javax.persistence.PersistenceException: org.hibernate.exception.JDBCConnectionException: could not inspect JDBC autocommit mode
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1365)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1293)
    at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:265)    
... 60 more
Caused by: org.hibernate.exception.JDBCConnectionException: could not inspect JDBC autocommit mode
    at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:131)
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:110)
    at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.isAutoCommit(LogicalConnectionImpl.java:395)
    at org.hibernate.engine.transaction.internal.TransactionCoordinatorImpl.afterNonTransactionalQuery(TransactionCoordinatorImpl.java:195)
    at org.hibernate.internal.SessionImpl.afterOperation(SessionImpl.java:565)
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1220)
    at org.hibernate.internal.QueryImpl.list(QueryImpl.java:101)
    at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:256)
... 70 more
Caused by: org.postgresql.util.PSQLException: This connection has been closed.
    at org.postgresql.jdbc2.AbstractJdbc2Connection.checkClosed(AbstractJdbc2Connection.java:712)
    at org.postgresql.jdbc2.AbstractJdbc2Connection.getAutoCommit(AbstractJdbc2Connection.java:678)
    at sun.reflect.GeneratedMethodAccessor138.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.apache.tomcat.jdbc.pool.ProxyConnection.invoke(ProxyConnection.java:126)
    at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:99)
    at org.apache.tomcat.jdbc.pool.DisposableConnectionFacade.invoke(DisposableConnectionFacade.java:63)
    at $Proxy66.getAutoCommit(Unknown Source)
    at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.isAutoCommit(LogicalConnectionImpl.java:392)

当这开始时,我得到一个

SQL Error: 0, SQLState: 08006 - An I/O error occured while sending to the backend.

但在那之后它只是:

SQL Error: 0, SQLState: 08003 - This connection has been closed.

问题是:我设置了 testOnBorrow,所以我希望只获得打开的连接。

如果这有帮助:池通常包含好连接和坏连接,随着时间的推移,问题似乎会得到解决,但我让服务器运行了 >12 小时,但仍然返回了坏连接。 重启后一切正常(一段时间)。

我已经对问题进行了更多调试,并且似乎池返回了错误的连接,例如如果在我在 DB 上杀死所有连接之后我得到:

SQL Error: 0, SQLState: 57P01

然后是通常的东西 - 从池中返回被终止的连接。 问题是:是应用问题吗?

我尝试通过 JMX 清除池,但这似乎没有任何效果。 另一个奇怪的事情是,即使应用程序显然没有做任何事情(通过线程转储检查),JMX bean 仍显示 7 个活动连接和 0 个空闲连接。当我执行一个需要访问数据库的请求时,我会立即得到响应(尽管没有可用的空闲连接),但之后 JMX 显示 7 个活动连接和 0 个空闲连接。

PS。也许我遗漏了一些明显的东西,这是我的连接管理问题?我正在使用通过 persistence.xml 配置的 JPA EntityManager,所以也许我做错了什么并且使用后连接没有正确关闭(返回)?

【问题讨论】:

  • 你是如何解决这个错误的?

标签: jpa guice guice-persist


【解决方案1】:

其实我怀疑是应用程序错误是对的。

Issue 730: Automatically started UnitOfWork is never ended 中描述的很好

使用 JpaPersistService 时,如果您尝试访问 EntityManager 在活动的 UnitOfWork 之外,Guice 将 自动为您启动一个。然而,正如 Guice 没有(和 不能)知道什么时候结束这个 UnitOfWork,它永远不会。

结果?有问题的线程将被同一个 EntityManager 卡住 在应用程序的整个生命周期中。这是一个糟糕的状态 应用程序运行,我们不可避免地耗尽了可用的 一段时间后记忆并崩溃。

这里真正的杀手锏是,当你做了 这个错误。唯一真正的提示是你得到 不同线程之间数据库中的数据不一致(由于 EMs 一级缓存)或应用程序内存消耗 继续增长。 在我的情况下,是池中的活动连接让我怀疑它,然后,当我打开详细日志记录时,我注意到应用程序根本没有从池中借用连接,而是重用了已经持有的连接未关闭的EntityManager。

其实这个问题有不少重复报告:http://code.google.com/p/google-guice/issues/list?can=1&q=UnitOfWork

【讨论】:

    【解决方案2】:

    一个狂野的镜头:代码here 表明,validationInterval 也被检查为testOnBorrow

    由于您将此值从默认的 30 秒设置为 5 分钟,这意味着在数据库断开连接后的 5 分钟内,您仍然可以获得该陈旧连接。如果您的数据库超时时间少于 5 分钟...运气不好。

    为了测试这个理论,您可以将validationInterval 设置为一个荒谬的低值。

    如果这有帮助(阅读:我们找到了正确的旋钮),您应该至少将其设置为比 DB 超时更短的时间。因此,当数据库决定删除空闲连接时,较低的validationInterval 将确保在下一次借用之前检查该连接。由于数据库服务器重新启动(即没有超时)而关闭的连接不会受到此解决方案的影响,但至少恢复正常状态的时间也较短。

    注意:我刚刚向 Google 索要了代码。我不知道这是实际代码还是古代代码。

    【讨论】:

    • actual Tomcat source也可以,不用看SpringSource的呃,复制一下。
    • 看起来像@A.H.很可能是在正确的轨道上:验证间隔也适用于 validate-on-borrow,至少在使用 Tomcat 的 jdbc-pool 时是这样。
    • 很抱歉这么大惊小怪,但我也不认为是这样。我已经让服务器在这种情况下运行超过 12 小时,并且每隔 10 分钟就有一个脚本调用它。一直以来,我每小时都会出现 2 或 3 个错误。我仍然会尝试设置一个较低的值,看看会发生什么。
    • 我已通过 JMX 将 validationInterval 设置为 1,而实例在池中连接不良时运行。它没有改变任何东西
    【解决方案3】:

    如果您已修改 META-INF/context.xml 以包含 validationQuery,则 Tomcat 可能会将旧定义缓存在 conf/engine/host/webapp.xml 下。关闭 Tomcat,删除该文件,然后重新启动 Tomcat。在 Tomcat 关闭时删除 work 目录也无妨。

    【讨论】:

    • 我不这么认为——它在多台服务器上发生了多次(中间有重新启动)。此外,自初始部署以来,我也没有修改 validationQuery。
    猜你喜欢
    • 1970-01-01
    • 2018-07-11
    • 1970-01-01
    • 2014-09-12
    • 2019-05-05
    • 2012-03-31
    • 2015-10-31
    • 2011-04-10
    • 2015-12-12
    相关资源
    最近更新 更多