Как правильно обрабатывать пул соединений Hikari с Doobie
Я использовал doobie (cats), чтобы подключиться к базе данных postgresql из приложения scalatra. Недавно я заметил, что приложение создает новый пул соединений для каждой транзакции. В конце концов я обошел это - см. Ниже, но этот подход сильно отличается от того, который использовался в разделе "Управление соединениями" книги Дуби, я надеялся, что кто-то может подтвердить, разумно ли это или есть лучший способ настройки до пула соединений.
Вот что у меня было изначально - это работает, но создает новый пул соединений для каждого соединения:
import com.zaxxer.hikari.HikariDataSource
import doobie.hikari.hikaritransactor.HikariTransactor
import doobie.imports._
val pgTransactor = HikariTransactor[IOLite](
"org.postgresql.Driver",
s"jdbc:postgresql://${postgresDBHost}:${postgresDBPort}/${postgresDBName}",
postgresDBUser,
postgresDBPassword
)
// every query goes via this function
def doTransaction[A](update: ConnectionIO[A]): Option[A] = {
val io = for {
xa <- pgTransactor
res <- update.transact(xa) ensuring xa.shutdown
} yield res
io.unsafePerformIO
}
Мое первоначальное предположение было, что проблема ensuring xa.shutdown
при каждом запросе, но удаление приводит к быстрому использованию соединений, пока не останется ни одного.
Это была попытка исправить проблему - позволил мне удалить ensuring xa.shutdown
, но все равно приводил к многократному открытию и закрытию пула соединений:
val pgTransactor: HikariTransactor[IOLite] = HikariTransactor[IOLite](
"org.postgresql.Driver",
s"jdbc:postgresql://${postgresDBHost}:${postgresDBPort}/${postgresDBName}",
postgresDBUser,
postgresDBPassword
).unsafePerformIO
def doTransaction[A](update: ConnectionIO[A]): Option[A] = {
val io = update.transact(pgTransactor)
io.unsafePerformIO
}
Наконец, я получил желаемое поведение, создав HikariDataSource
объект, а затем передать его в HikariTransactor
конструктор:
val dataSource = new HikariDataSource()
dataSource.setJdbcUrl(s"jdbc:postgresql://${postgresDBHost}:${postgresDBPort}/${postgresDBName}")
dataSource.setUsername(postgresDBUser)
dataSource.setPassword(postgresDBPassword)
val pgTransactor: HikariTransactor[IOLite] = HikariTransactor[IOLite](dataSource)
def doTransaction[A](update: ConnectionIO[A], operationDescription: String): Option[A] = {
val io = update.transact(pgTransactor)
io.unsafePerformIO
}
2 ответа
Вы можете сделать что-то вроде этого:
val xa = HikariTransactor[IOLite](dataSource).unsafePerformIO
и передать его в свои репозитории..transact
применяет границы транзакции, как Slick
"s .transactionally
, Например:
def interactWithDb = {
val q: ConnectionIO[Int] = sql"""..."""
q.transact(xa).unsafePerformIO
}
Да, ответ от Раду попадает в проблему. HikariTransactor
(основной HikariDataSource
действительно) имеет внутреннее состояние, поэтому его построение является побочным эффектом; и вы хотите сделать это один раз, когда ваша программа запускается, и передавать ее по мере необходимости. Итак, ваше решение работает, просто отметьте побочный эффект.
Также, как уже отмечалось, я не слежу за SO … попробуйте канал Gitter или откройте вопрос, если у вас есть вопросы.:-)