Как правильно обрабатывать пул соединений 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 или откройте вопрос, если у вас есть вопросы.:-)

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