Соединение закрыто с data-envers

Я работаю с data-envers над своим Spring-Boot проектом в среде разработки. Когда микросервис проработает примерно 8 часов, соединение с базой данных перестанет работать (продолжают работать только те запросы, которые используются с data-envers, то есть любой запрос с JPA, например).

Каждый раз, когда я перезапускаю микросервис (pod), соединение с базой данных снова работает, и запросы, сделанные с помощью data-envers, снова работают (еще около 8 часов).

Я прочитал много статей и сообщений о SO, но они мне не помогли. Я добавил в свой файл .yml следующие свойства:

testOnBorrow : true
validationQuery : SELECT 1

я также добавил

url: jdbc:mysql://localhost:3306/mydatabase?autoReconnect=true

и не могу переподключиться.

Обновил версию Hikari, так как тоже читал, что может быть так, но это не так. Моя data-envers версия 2.7.6

Любые идеи, как предотвратить закрытие или автоматическое повторное подключение соединения?

Это мой .yml

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mydatabase?autoReconnect=true
    username: X
    password: X
    testOnBorrow : true
    validationQuery : SELECT 1
    hikari:
      connection-timeout: 20000
      maximum-pool-size: 5

и трассировка стека:

java.sql.SQLException: Connection is closed
    at com.zaxxer.hikari.pool.ProxyConnection$ClosedConnection.lambda$getClosedConnection$0(ProxyConnection.java:515) ~[HikariCP-5.0.0.jar!/:na]
    at com.zaxxer.hikari.pool.ProxyConnection$ClosedConnection$$Lambda$718/0x00000000558ea900.invoke(Unknown Source) ~[na:na]
    at com.sun.proxy.$Proxy105.prepareStatement(Unknown Source) ~[na:na]
    at com.zaxxer.hikari.pool.ProxyConnection.prepareStatement(ProxyConnection.java:337) ~[HikariCP-5.0.0.jar!/:na]
    at com.zaxxer.hikari.pool.HikariProxyConnection.prepareStatement(HikariProxyConnection.java) ~[HikariCP-5.0.0.jar!/:na]
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$5.doPrepare(StatementPreparerImpl.java:149) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:176) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareQueryStatement(StatementPreparerImpl.java:151) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:2122) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2059) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2037) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.loader.Loader.doQuery(Loader.java:956) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:357) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.loader.Loader.doList(Loader.java:2868) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.loader.Loader.doList(Loader.java:2850) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2682) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.loader.Loader.list(Loader.java:2677) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:540) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:400) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:219) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1459) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1649) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1617) ~[hibernate-core-5.6.12.Final.jar!/:5.6.12.Final]
    at org.hibernate.envers.query.internal.impl.AbstractAuditQuery.buildAndExecuteQuery(AbstractAuditQuery.java:106) ~[hibernate-envers-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.envers.query.internal.impl.RevisionsOfEntityQuery.getQueryResults(RevisionsOfEntityQuery.java:173) ~[hibernate-envers-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.envers.query.internal.impl.RevisionsOfEntityQuery.list(RevisionsOfEntityQuery.java:136) ~[hibernate-envers-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.envers.query.internal.impl.AbstractAuditQuery.getResultList(AbstractAuditQuery.java:112) ~[hibernate-envers-5.6.8.Final.jar!/:5.6.8.Final]

Есть еще одна трассировка стека:

com.mysql.cj.jdbc.exceptions.CommunicationsException: The last packet successfully received from the server was 35,850,470 milliseconds ago. The last packet sent successfully to the server was 35,850,472 milliseconds ago. is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.
    at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:174) ~[mysql-connector-java-8.0.28.jar!/:8.0.28]
    at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64) ~[mysql-connector-java-8.0.28.jar!/:8.0.28]
    at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953) ~[mysql-connector-java-8.0.28.jar!/:8.0.28]
    at com.mysql.cj.jdbc.ClientPreparedStatement.executeQuery(ClientPreparedStatement.java:1009) ~[mysql-connector-java-8.0.28.jar!/:8.0.28]
    at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52) ~[HikariCP-4.0.3.jar!/:na]
    at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java) ~[HikariCP-4.0.3.jar!/:na]
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:57) ~[hibernate-core-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.loader.Loader.getResultSet(Loader.java:2322) ~[hibernate-core-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2075) ~[hibernate-core-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2037) ~[hibernate-core-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.loader.Loader.doQuery(Loader.java:956) ~[hibernate-core-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:357) ~[hibernate-core-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.loader.Loader.doList(Loader.java:2868) ~[hibernate-core-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.loader.Loader.doList(Loader.java:2850) ~[hibernate-core-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2682) ~[hibernate-core-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.loader.Loader.list(Loader.java:2677) ~[hibernate-core-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:540) ~[hibernate-core-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:400) ~[hibernate-core-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:219) ~[hibernate-core-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1459) ~[hibernate-core-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1649) ~[hibernate-core-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1617) ~[hibernate-core-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.envers.query.internal.impl.AbstractAuditQuery.buildAndExecuteQuery(AbstractAuditQuery.java:106) ~[hibernate-envers-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.envers.query.internal.impl.RevisionsOfEntityQuery.getQueryResults(RevisionsOfEntityQuery.java:173) ~[hibernate-envers-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.envers.query.internal.impl.RevisionsOfEntityQuery.list(RevisionsOfEntityQuery.java:136) ~[hibernate-envers-5.6.8.Final.jar!/:5.6.8.Final]
    at org.hibernate.envers.query.internal.impl.AbstractAuditQuery.getResultList(AbstractAuditQuery.java:112) ~[hibernate-envers-5.6.8.Final.jar!/:5.6.8.Final]

ОБНОВЛЕНО:

Это мой сервис, который использует auditQuery

public List<Revision> getRevisions(Integer id) {
        AuditQuery auditQuery = null;
        List<Revision> revisionList = new ArrayList<Revision>();
        //Find all modifications
        try {       
            auditQuery = auditReader.createQuery()
                .forRevisionsOfEntityWithChanges(MyEntity.class, true)
                .addOrder(AuditEntity.revisionNumber().desc());
            
        }

        catch (Exception exception) {
            log.error("Can not get revisions. Return empty list without revisions.");
            log.error(exception.getMessage());
            return new ArrayList<Revision>();
        }
        
        List<Object[]> result = auditQuery.getResultList();

Последняя строка - это когда я получаю сообщение об ошибке при закрытии соединения.

И это класс конфигурации

@Configuration
public class AuditConfiguration {

    private final EntityManagerFactory entityManagerFactory;

    AuditConfiguration(EntityManagerFactory entityManagerFactory) {
        this.entityManagerFactory = entityManagerFactory;
    }

    @Bean
    AuditReader auditReader() {
        return AuditReaderFactory.get(entityManagerFactory.createEntityManager());
    }
}
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
60
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

основываясь на ваших трассировках стека, я бы сказал, что основная причина находится где-то в вашем приложении.

I. Следующие настройки не влияют на опрос подключения hikari:

testOnBorrow : true
validationQuery : SELECT 1

эти настройки действительны для Пула соединений JDBC Tomcat, обратите внимание, что префикс конфигурации для этих настроек должен быть spring.datasource.tomcat.

если вам нужно настроить запрос проверки для hikari пула соединений, допустимым параметром конфигурации является spring.datasource.hikari.connection-test-query, однако в большинстве случаев это не требуется, более того, hikari автор настоятельно не рекомендует использовать эту функцию:

Если ваш драйвер поддерживает JDBC4, мы настоятельно рекомендуем не устанавливать это свойство. Это для «устаревших» драйверов, которые не поддерживают JDBC4 Connection.isValid() API. Это запрос, который будет выполняться непосредственно перед тем, как вам будет предоставлено соединение из пула, чтобы проверить, что соединение с базой данных все еще активно. Опять же, попробуйте запустить пул без этого свойства, HikariCP зарегистрирует ошибку, если ваш драйвер не совместим с JDBC4, чтобы сообщить вам об этом. По умолчанию: нет

II. вы также не должны использовать autoReconnect=true :

Должен ли драйвер пытаться восстановить устаревшие и/или мертвые соединения? Если этот параметр включен, драйвер выдает исключение для запросов, отправленных по устаревшему или мертвому соединению, которые относятся к текущей транзакции, но попытается переподключиться до того, как следующий запрос будет отправлен для соединения в новой транзакции. Использование этой функции не рекомендуется, поскольку она имеет побочные эффекты, связанные с состоянием сеанса и согласованностью данных, когда приложения не обрабатывают исключения SQLException должным образом, и предназначена для использования только в том случае, если вы не можете настроить свое приложение для обработки исключений SQLException, возникающих в результате мертвые и устаревшие соединения должным образом. В качестве альтернативы, в качестве последнего варианта, попробуйте установить для переменной сервера MySQL 'wait_timeout' высокое значение, а не значение по умолчанию, равное 8 часам.

III. внимательно посмотрите на следующую трассировку стека:

java.sql.SQLException: Connection is closed
    at com.zaxxer.hikari.pool.ProxyConnection$ClosedConnection.lambda$getClosedConnection$0(ProxyConnection.java:515) ~[HikariCP-5.0.0.jar!/:na]
    at com.zaxxer.hikari.pool.ProxyConnection$ClosedConnection$$Lambda$718/0x00000000558ea900.invoke(Unknown Source) ~[na:na]
    at com.sun.proxy.$Proxy105.prepareStatement(Unknown Source) ~[na:na]

обратите внимание, что исключение было выдано в классе com.zaxxer.hikari.pool.ProxyConnection$ClosedConnection, hikari заменяет реальный экземпляр соединения jdbc прокси-сервером на основе com.zaxxer.hikari.pool.ProxyConnection$ClosedConnection, когда соединение JDBC либо закрывается , либо получает фатальную ошибку, таким образом, определенно ясно, что ваше приложение продолжает использовать JDBC соединение после того, как оно было закрыто или получить фатальную ошибку


УПД.

Ну, теперь понятно, что происходит. Конфигурация ниже:

@Configuration
public class AuditConfiguration {

    private final EntityManagerFactory entityManagerFactory;

    AuditConfiguration(EntityManagerFactory entityManagerFactory) {
        this.entityManagerFactory = entityManagerFactory;
    }

    @Bean
    AuditReader auditReader() {
        return AuditReaderFactory.get(entityManagerFactory.createEntityManager());
    }
}

создает один экземпляр EntityManager для всего приложения и привязывает его к одному экземпляру AuditReader, поэтому вы никогда не освобождаете базовое соединение JDBC с пулом и не получаете эти глупые ошибки.

Быстрое решение состоит в том, чтобы удалить эту преждевременную оптимизацию и переписать класс обслуживания, что-то вроде (здесь я полагаю, что EMF управляется spring):

@Autowired
private EntityManager entityManager;

@Transactional
public List<Revision> getRevisions(Integer id) {
    AuditReader auditReader = AuditReaderFactory.get(entityManager);
    AuditQuery auditQuery = null;
    List<Revision> revisionList = new ArrayList<Revision>();

хорошо, я понимаю, но что я могу сделать, чтобы решить эту проблему?

Talenel 26.01.2023 07:21

трудно сказать, так как вы не делитесь кодом

Andrey B. Panfilov 26.01.2023 07:24

Я обновил сообщение с моим сервисом и кодом конфигурации. Пожалуйста, скажите мне, если вам нужно что-то еще

Talenel 26.01.2023 07:33

@Talenel, пожалуйста, проверьте UPD.

Andrey B. Panfilov 26.01.2023 08:08

большое спасибо, я попробую это решение, если это сработает, я проверю это, чтобы получить правильный ответ, но мне нужно подождать не менее 8 часов, чтобы увидеть, работает ли

Talenel 26.01.2023 08:15

Другие вопросы по теме