пытаясь найти утечку соединения БД в моем коде, используя Spring / JPA / Hikari - PullRequest
0 голосов
/ 27 апреля 2018

У меня проблема с веб-приложением Spring, которое периодически вызывает ошибку при извлечении соединения из пула соединений. В конце концов в журналах я вижу записи вроде:

  • Причина: javax.persistence.PersistenceException: org.hibernate.exception.JDBCConnectionException: невозможно получить соединение JDBC
  • Причина: java.sql.SQLTransientConnectionException: HikariPool-1 - Соединение недоступно, время запроса истекло после 30000 мс.

Единственный способ восстановить, который я обнаружил, как только он достиг этой точки, это перезапустить Tomcat.

Я думаю, что наиболее вероятное объяснение состоит в том, что у меня где-то есть код, который не очищает должным образом его соединение - возвращает его в Hikari, оставляет что-то открытым, чтобы Spring не мог его очистить, и т. Д.

Для устранения неполадок я установил для своего утечки конфигурации hikari leakDetectionThreshold значение 5000 мс и включил ведение журнала. После этого я вижу записи в журнале, такие как

2018-04-24 19:53:56 WARN  ProxyLeakTask:87 - Connection leak detection 
triggered for org.postgresql.jdbc.PgConnection@664ec666, stack trace 
follows
java.lang.Exception: Apparent connection leak detected
    at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122)
    at org.hibernate.internal.NonContextualJdbcConnectionAccess.obtainConnection(NonContextualJdbcConnectionAccess.java:35)
    at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:99)
    at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getPhysicalConnection(LogicalConnectionManagedImpl.java:129)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.connection(StatementPreparerImpl.java:47)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$5.doPrepare(StatementPreparerImpl.java:146)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:172)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareQueryStatement(StatementPreparerImpl.java:148)
    at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1940)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1909)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1887)
    at org.hibernate.loader.Loader.doQuery(Loader.java:932)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:349)
    at org.hibernate.loader.Loader.doList(Loader.java:2615)
    at org.hibernate.loader.Loader.doList(Loader.java:2598)
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2430)
    at org.hibernate.loader.Loader.list(Loader.java:2425)
    at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:335)
    at org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:2129)
    at org.hibernate.internal.AbstractSharedSessionContract.list(AbstractSharedSessionContract.java:981)
    at org.hibernate.query.internal.NativeQueryImpl.doList(NativeQueryImpl.java:147)
    at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1398)
    at org.hibernate.query.internal.AbstractProducedQuery.getSingleResult(AbstractProducedQuery.java:1444)
    at sun.reflect.GeneratedMethodAccessor191.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$DeferredQueryInvocationHandler.invoke(SharedEntityManagerCreator.java:379)
    at com.sun.proxy.$Proxy163.getSingleResult(Unknown Source)
    at com.mycompany.web.jpa.util.DBHelper.getPagedMappedDbResults(DBHelper.java:76)
    at com.mycompany.web.jpa.repository.TaskRepositoryImpl.findTaskDetailsByStepIdAndIdIn(TaskRepositoryImpl.java:245)
......

Так что это обнаруживает возможную утечку. Может быть, ложный положительный результат? Но это также единственный класс в моем приложении, который осуществляет доступ к базе данных вне стандартного шаблона службы / хранилища, часто используемого в приложениях Spring, поэтому он кажется вероятным виновником, и на данный момент это мой лучший лидер.

В любом случае, последний фрагмент небиблиотечного кода, который я вижу в трассировке (т. Е. Материал, который я написал, поэтому, скорее всего, является причиной утечки!), Это мой метод DBHelper :: getPagedMappedDbResults, соответствующий бит включен здесь:

    Query q = entityManager.createNativeQuery(countQueryText);
    setQueryParameters(q, parameters);
    long numActualResults = 0;
    try {
        numActualResults = ((Number)q.getSingleResult()).longValue(); // line 76
    } catch (Exception e) {
        System.out.println("just in case: " + e);
    }

Таким образом, в основном я создаю объект Query из моего экземпляра EntityManager, устанавливаю некоторые параметры и запускаю его, чтобы получить некоторые результаты.

Есть ли что-то, что мне нужно делать с объектом Query, когда я закончу с ним? q.cleanup ()? Я не вижу ничего подобного в чтении документов, но разве я не веду домашнюю работу на этом ресурсе?

Сам EntityManager создается из аннотации @Autowired. Насколько я понимаю, если я не «создал» его для создания экземпляра и вместо этого позволил инфраструктуре Spring автоматически подключить его, то Spring выполнит любую необходимую очистку. Это правильно? Или мне нужно выполнить очистку после использования entityManager?

Сведения о версии:

  • Tomcat 8 / Java 8
  • Spring 5.0.0.RELEASE
  • Spring Data Kay-RELEASE
  • Hibernate 5.2.3. Финал
  • Хикари 2.4.5

Любые советы или предложения будут с благодарностью, спасибо!

1 Ответ

0 голосов
/ 27 апреля 2018

Что такое запрос? Это тяжело? Может быть, у вас здесь тупик? Управление подключением выглядит нормально. Вы не получаете соединение явно, поэтому нет необходимости освобождать его. Возможно, запрос выполняется долго, поэтому Hibernate не может его завершить и освободить соединение.

Также вы можете проверить количество открытых соединений на стороне БД. Также проведите некоторый анализ на этой стороне.

...