Java/Kotlin Flyway + MyBatis + утечка соединений HikariCP

Вопрос или проблема

Я разрабатываю чистое приложение на Kotlin. В моем приложении мне нужно получить доступ к базе данных.

Мой текущий стек для этого:

  • mybatis версия = “3.5.16”
  • flyway версия = “10.19.0”
  • hikari версия = “6.0.0”

Для разработки я использую Intellij Idea. Это важно. После некоторого времени разработки у меня возникает ошибка в базе данных – слишком много соединений. Я думаю, это связано с отладкой и завершением процесса. Я думаю, что это мешает аккуратно закрыть соединение.

Я не нашел никакой причины. Когда я делаю это вручную много раз, я не замечал, что соединение утекло (show processlist; пусто)

Вот код, который я выполняю в начале приложения:

       
class DB(props: AppProperties) { 
    private val hikariSource: HikariDataSource
    private var batisSessionFactory: SqlSessionFactory

    // Конструктор
    init {
         // HikariCP
        hikariSource: HikariDataSource = HikariDataSource()
        hikariSource.jdbcUrl = props.databaseUrl
        hikariSource.username = props.databaseUser
        hikariSource.password = props.databasePass
        hikariSource.maximumPoolSize = 5
        hikariSource.isAutoCommit = true

        // Flyway
        val config = Flyway
            .configure()
            .dataSource(dataSource)
            .baselineOnMigrate(true)

        val flyway = Flyway(config)
        flyway.migrate()

        // MyBatis
         batisSessionFactory = createSqlSessionFactory(hikariSource).apply {
            configuration.addMapper(UserDao::class.java)
        }
    }

    fun <T> withUserDao(autoCommit: Boolean = true, block: UserDao.() -> T) : T {
        return batisSessionFactory.openSession(autoCommit).use {
            val userDao = it.getMapper(UserDao::class.java)
            block(userDao)
        }
    }

    private fun createSqlSessionFactory(dataSource: DataSource): SqlSessionFactory {
        val configuration = Configuration()

        // Передайте свой пользовательский DataSource в окружение
        val environment = org.apache.ibatis.mapping.Environment(
            props.environment,
            org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory(),
            dataSource
        )
        configuration.environment = environment
        return SqlSessionFactoryBuilder().build(configuration)
    }
}

Вот и всё. Этот простой код вызывает утечки соединений. И я не уверен, кто отвечает за эту утечку. Hikari? Flyway? MyBatis?

Для разработчиков на Java о функции withUserDao()

  • открывает сессию batis как try-with-resources в стиле Java
  • внутри блока try получает маппер для UserDao и передает его в лямбду внешнего блока
  • после работы с UserDao – закрывает сессию batis

Ответ или решение

Разбор проблемы утечек соединений при использовании Flyway, MyBatis и HikariCP в Kotlin

В процессе разработки ваших приложений на Kotlin, взаимодействующих с базой данных, вы столкнулись с проблемой утечек соединений, проявляющейся в ошибках "слишком много соединений". Давайте рассмотрим возможные причины этой проблемы и предложим пути её решения.

1. Понимание используемых технологий

Ваш стек состоит из трех ключевых компонентов:

  • MyBatis (версия 3.5.16): ORM-фреймворк, который управляет соединениями и маппингом данных.
  • Flyway (версия 10.19.0): инструмент для миграции баз данных, использующий соединение с БД для выполнения миграций.
  • HikariCP (версия 6.0.0): высокопроизводительный пул соединений, отвечающий за эффективное управление соединениями с БД.

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

2. Возможные причины утечек соединений

  1. Управление соединениями:

    • Вы используете openSession(autoCommit) в методе withUserDao. Эта функция возвращает сессию MyBatis, которая должна закрываться после завершения работы. Однако если поток выполнения прерывается до закрытия, могут возникнуть неосвобожденные соединения.
  2. Проблемы с Flyway:

    • Flyway выполняет миграции в рамках одного соединения. Если миграции происходят при частых изменениях вашего кода, это может также привести к лишним соединениям.
  3. Проблемы в процессе отладки:

    • Как вы уже заметили, если вы часто останавливаете и перезапускаете приложение в процессе отладки, это может привести к неисправно закрытым соединениям. HikariCP не всегда успевает корректно закрыть соединения при аварийном завершении.

3. Рекомендации по исправлению проблемы

  • Убедитесь, что все соединения закрываются. Использование use с сессией MyBatis – это хорошая практика. Однако следует иметь в виду, что если внутри блока use происходит исключение, соединение может не освободиться. Рассмотрите возможность обработки исключений:

    fun <T> withUserDao(autoCommit: Boolean = true, block: UserDao.() -> T): T {
        return batisSessionFactory.openSession(autoCommit).use { session ->
            val userDao = session.getMapper(UserDao::class.java)
            try {
                block(userDao)
            } catch (e: Exception) {
                // Логируйте исключение или обрабатывайте его
                throw e
            }
        }
    }
  • Проверка конфигурации HikariCP: Проверьте настройки вашего пула соединений. Убедитесь, что параметры, такие как максимальное количество соединений, правильны и соответствуют требованиям вашего приложения. Например, вы можете уменьшить значение maximumPoolSize, чтобы избежать создания слишком большого количества соединений.

  • Заботьтесь о Flyway: Убедитесь, что миграции не выполняются без необходимости. Можно выполнять миграции только при необходимости, а не при каждом запуске приложения.

  • Мониторинг соединений: Используйте возможности HikariCP для мониторинга состояния соединений. Например, настраивайте логи соединений, чтобы лучше понять, какие соединения остаются открытыми.

4. Заключение

Утечки соединений могут иметь много причин, и важно проводить тщательный анализ кода и конфигураций вашего приложения. Рекомендуется следить за состоянием соединений и ввести механизмы, позволяющие выявлять и устранять такие проблемы.

Если все эти рекомендации не устранют проблему, возможно, стоит подумать о дополнительной диагностике с использованием инструментов профилирования JVM, чтобы увидеть, где именно ваши соединения не закрываются.

Надеюсь, эти рекомендации помогут вам разобраться с вашей проблемой с утечками соединений в приложении. Не забывайте, что тщательное управление ресурсами – ключ к стабильной и надежной работе приложений.

Оцените материал
Добавить комментарий

Капча загружается...