数据库重置后重新连接 HikariCP

Reconnect HikariCP after DB reset

这类似于我大约 2 年前发布的 问题。

我有一个连续从数据库中读取的简单测试。我 stop/start 我的 SQL 服务器实例。

@Test
public void testConnectionReset() {
while (true) {
  try {
   simpleRead();
  }
  catch (Exception e) {
    e.printStackStrace();
  }
}

@com.google.inject.persist.Transactional
protected void simpleRead() {
  dao = new MyDao(....)
  dao.findBy(...)
}

如果错误发生在simpleRead()中,则全部正确处理。如果错误发生在testConnectionReset(),我有如下stackTrace,重启数据库后没有reconnect。

[2017-Dec-21 10:47:26] - [ERROR] - I/O Error: Connection reset by peer: socket write error
    org.hibernate.TransactionException: Unable to rollback against JDBC Connection
        at org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor.rollback(AbstractLogicalConnectionImplementor.java:121)
        at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.rollback(JdbcResourceLocalTransactionCoordinatorImpl.java:250)
        at org.hibernate.engine.transaction.internal.TransactionImpl.rollback(TransactionImpl.java:100)
        at com.google.inject.persist.jpa.JpaLocalTxnInterceptor.rollbackIfNecessary(JpaLocalTxnInterceptor.java:153)
        at com.google.inject.persist.jpa.JpaLocalTxnInterceptor.invoke(JpaLocalTxnInterceptor.java:74)
        at temp.testConnection(TestConnection.java:45)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84)
        at org.testng.internal.Invoker.invokeMethod(Invoker.java:714)
        at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:901)
        at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1231)
        at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127)
        at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111)
        at org.testng.TestRunner.privateRun(TestRunner.java:767)
        at org.testng.TestRunner.run(TestRunner.java:617)
        at org.testng.SuiteRunner.runTest(SuiteRunner.java:334)
        at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:329)
        at org.testng.SuiteRunner.privateRun(SuiteRunner.java:291)
        at org.testng.SuiteRunner.run(SuiteRunner.java:240)
        at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
        at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
        at org.testng.TestNG.runSuitesSequentially(TestNG.java:1224)
        at org.testng.TestNG.runSuitesLocally(TestNG.java:1149)
        at org.testng.TestNG.run(TestNG.java:1057)
        at org.testng.remote.AbstractRemoteTestNG.run(AbstractRemoteTestNG.java:132)
        at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:236)
        at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:81)
    Caused by: java.sql.SQLException: Connection is closed
        at com.zaxxer.hikari.pool.ProxyConnection$ClosedConnection.lambda$getClosedConnection[=11=](ProxyConnection.java:490)
        at com.sun.proxy.$Proxy84.rollback(Unknown Source)
        at com.zaxxer.hikari.pool.ProxyConnection.rollback(ProxyConnection.java:377)
        at com.zaxxer.hikari.pool.HikariProxyConnection.rollback(HikariProxyConnection.java)
        at org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor.rollback(AbstractLogicalConnectionImplementor.java:116)
        ... 29 more

我目前的解决方法是将 com.zaxxer.hikari.aliveBypassWindowMs 设置为 0,每次在池返回之前强制进行连接检查。我发现很难相信这是一个 Hikari 错误,因为我没有看到其他人有这个问题,所以也许它是我缺少的其他配置。

我的筹码是

这是 guice-persist 中的错误。它与 this 一个密切相关。简单的事件流程是这样的

  1. Hikari 在池中有一个有效连接
  2. 与数据库的连接丢失
  3. 调用 Guice-presist @Transactional,并从池中拉取连接(未选中)。
  4. Guice 执行 txn.begin(),抛出异常但无法结束 unitOfWork / closeConnection。池仍然处于错误状态。

我找到的解决方法是以下之一

  1. 手动处理 unitOfWork。
  2. 通过将 aliveBypassWindowMs 设置为 0,要求 Hikari 每次从连接池返回时检查连接。
  3. 通过在必要时结束 unitOfWork 直接修改有问题的 class (JpaLocalTxnInterceptor)。