Моя команда столкнулась со странной серией ошибок во время развертывания безобидного на вид изменения кода на этой неделе, и я ищу указания на то, что могло произойти. Нам не удалось воспроизвести проблему в непроизводственной среде, и мы не хотим повторно развертывать ее, пока не поймем, в чем проблема.
Мы используем Java Spring Boot, Hibernate, Postgres и Hikari. Изменение кода было нефункциональным рефакторингом для разделения большого класса обслуживания FooService
на два класса FooReadService
и FooUpsertService
. Все три из них были помечены на уровне класса с помощью @Transactional
.
Ниже приведены семейства журналов, которые мы наблюдали сразу после развертывания этих изменений. Как видите, первые ошибки связаны с невозможностью проверки соединений с базой данных Hikari. Последующие ошибки связаны с невозможностью установить соединение с базой данных, а затем выполнить запись в транзакциях только для чтения.
Интересно, что ошибки, связанные с выполнением операций записи в транзакциях только для чтения, возникали в нескольких кодовых путях, совершенно не связанных с этим конкретным изменением. Чтения, казалось, работали успешно во время инцидента. Это первый раз, когда мы видим подобные ошибки после примерно года работы в продакшене.
теории
- Что-то, связанное с разделением одного класса с аннотацией
@Transactional
на два класса, вызывает проблемы с подключением к базе данных. Возможно, границы транзакций не используются повторно? Мы не меняли однако поведение распространения по умолчанию дляREQUIRED
. - Что-то совершенно не связанное с этим PR произошло в нашем конвейере развертывания, что вызвало проблемы с этим конкретным развертыванием.
- Что-то, что проявляется только при большом объеме запросов. (Объем запросов был типичным во время этого окна развертывания, но это может объяснить разницу между локальным и производственным поведением.)
Вопросы
- Каковы возможные основные причины ошибки проверки соединения с Hikari?
- Являются ли все остальные ошибки просто последующими последствиями этой ошибки? Как может быть невозможно выполнить ошибки SELECT FOR UPDATE из-за того, что не удалось проверить ошибки подключения?
- Какие изменения логики приложения Java могут вызвать такие ошибки?
- По каким причинам юнит-тесты, интеграционные тесты, локальное тестирование разработки будут успешными, а продакшн-тесты потерпят неудачу? (Разные конфигурации среды, разные объемы трафика...)
- Что мы можем сделать, чтобы доказать или опровергнуть приведенные выше теории?
Мой следующий шаг — смоделировать гораздо больший трафик к локальному экземпляру с применением этого изменения и посмотреть, может ли это привести к проявлению проблемы. Но был бы очень признателен за любые указатели.
Журналы во время инцидента
24.03.2021, 15:30:38 — не удалось проверить подключение
com.zaxxer.hikari.pool.PoolBase: HikariPool-1 - Failed to validate connection org.postgresql.jdbc.PgConnection@3e1fe46b (This connection has been closed.). Possibly consider using a shorter maxLifetime value.
24.03.2021, 15:30:53 — не удалось установить соединение JDBC
org.springframework.dao.DataAccessResourceFailureException: Unable to acquire JDBC Connection; nested exception is org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection
2021-03-24 15:30:55 — не удалось открыть JPA EntityManager для транзакции
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection
2021-03-24 10:35:54 — невозможно выполнить SELECT FOR UPDATE в транзакции только для чтения
o.h.engine.jdbc.spi.SqlExceptionHelper: ERROR: cannot execute SELECT FOR UPDATE in a read-only transaction
org.springframework.orm.jpa.JpaSystemException: could not extract ResultSet; nested exception is org.hibernate.exception.GenericJDBCException: could not extract ResultSet