Question: How to correctly handle Hikari connection pool with Doobie

Question

How to correctly handle Hikari connection pool with Doobie

Answers 2
Added at 2017-11-08 12:11
Tags
Question

I've been using doobie (cats) to connect to a postgresql database from a scalatra application. Recently I noticed that the app was creating a new connection pool for every transaction. I eventually worked around it - see below, but this approach is quite different from that taken in the 'managing connections' section of the book of doobie, I was hoping someone could confirm whether it is sensible or whether there is a better way of setting up the connection pool.

Here's what I had initially - this works but creates a new connection pool on every connection:

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
}

My initial assumption was that the problem was having ensuring xa.shutdown on every request, but removing it results in connections quickly being used up until there are none left.

This was an attempt to fix the problem - enabled me to remove ensuring xa.shutdown, but still resulted in the connection pool being repeatedly opened and closed:

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
}

Finally, I got the desired behaviour by creating a HikariDataSource object and then passing it into the HikariTransactor constructor:

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
}
Answers to

How to correctly handle Hikari connection pool with Doobie

nr: #1 dodano: 2017-11-10 14:11

You can do something like this:

val xa = HikariTransactor[IOLite](dataSource).unsafePerformIO

and pass it to your repositories. .transact applies the transaction boundaries, like Slick's .transactionally. E.g.:

def interactWithDb = {
  val q: ConnectionIO[Int] = sql"""..."""
  q.transact(xa).unsafePerformIO
}
nr: #2 dodano: 2017-11-10 17:11

Yes, the response from Radu gets at the problem. The HikariTransactor (the underlying HikariDataSource really) has internal state so constructing it is a side-effect; and you want to do it once when your program starts and pass it around as needed. So your solution works, just note the side-effect.

Also, as noted, I don't monitor SO … try the Gitter channel or open an issue if you have questions. :-)

Source Show
◀ Wstecz