Skip to content

Commit

Permalink
Split methods for HikariTransactor constructed with two effects
Browse files Browse the repository at this point in the history
Partially revert the change in #1939 so that existing code don't fail to compile.
Methods which construct HikariTransactor in a different effect now has the suffix "...SeparateEffect". The original single-effect methods are now implemented in terms of the separate-effect methods.
  • Loading branch information
jatcwang committed Dec 18, 2023
1 parent 9fa2f52 commit b3377af
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 46 deletions.
2 changes: 1 addition & 1 deletion modules/docs/src/main/mdoc/docs/14-Managing-Connections.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ object HikariApp extends IOApp {
config.setPassword("")
config
}
xa <- HikariTransactor.fromHikariConfig[IO, IO](hikariConfig)
xa <- HikariTransactor.fromHikariConfig[IO](hikariConfig)
} yield xa


Expand Down
186 changes: 141 additions & 45 deletions modules/hikari/src/main/scala/doobie/hikari/HikariTransactor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import java.util.Properties
import java.util.concurrent.{ScheduledExecutorService, ThreadFactory}

import cats.effect.implicits._
import cats.effect.kernel.{ Async, Resource, Sync }
import cats.effect.kernel.{Async, Resource, Sync}
import com.zaxxer.hikari.metrics.MetricsTrackerFactory
import com.zaxxer.hikari.{HikariConfig, HikariDataSource}
import javax.sql.DataSource
Expand All @@ -20,7 +20,7 @@ object HikariTransactor {

/** Construct a `HikariTransactor` from an existing `HikariDatasource`. */
def apply[M[_]] = new HikariTransactorPartiallyApplied[M]

class HikariTransactorPartiallyApplied[M[_]] {
def apply(
hikariDataSource: HikariDataSource,
Expand All @@ -30,9 +30,19 @@ object HikariTransactor {
Transactor.fromDataSource[M](hikariDataSource, connectEC, logHandler)
}
}

/** Resource yielding an unconfigured `HikariTransactor`. */
def initial[M0[_]: Sync, M[_]: Async](
def initial[M[_] : Async](
connectEC: ExecutionContext,
logHandler: Option[LogHandler[M]] = None
): Resource[M, HikariTransactor[M]] = initialSeparateEffect[M, M](connectEC, logHandler)

/** Similar to [[initial]], but with a separate effect for the construction of the Transactor
*
* @tparam M0 the effect to construct the [[HikariTransactor]] in
* @tparam M the effect under which the [[HikariTransactor]] runs
*/
def initialSeparateEffect[M0[_] : Sync, M[_] : Async](
connectEC: ExecutionContext,
logHandler: Option[LogHandler[M]] = None
): Resource[M0, HikariTransactor[M]] = {
Expand All @@ -43,7 +53,40 @@ object HikariTransactor {
/** Resource yielding a new `HikariTransactor` configured with the given Config.
* Unless you have a good reason, consider using `fromConfig` which creates the `connectEC` for you.
*/
def fromConfigCustomEc[M0[_]: Sync, M[_]: Async](
def fromConfigCustomEc[M[_] : Async](
config: Config,
connectEC: ExecutionContext,
logHandler: Option[LogHandler[M]] = None,
dataSource: Option[DataSource] = None,
dataSourceProperties: Option[Properties] = None,
healthCheckProperties: Option[Properties] = None,
healthCheckRegistry: Option[Object] = None,
metricRegistry: Option[Object] = None,
metricsTrackerFactory: Option[MetricsTrackerFactory] = None,
scheduledExecutor: Option[ScheduledExecutorService] = None,
threadFactory: Option[ThreadFactory] = None,
): Resource[M, HikariTransactor[M]] = {
fromConfigCustomEcSeparateEffect[M, M](
config = config,
connectEC = connectEC,
logHandler = logHandler,
dataSource = dataSource,
dataSourceProperties = dataSourceProperties,
healthCheckProperties = healthCheckProperties,
healthCheckRegistry = healthCheckRegistry,
metricRegistry = metricRegistry,
metricsTrackerFactory = metricsTrackerFactory,
scheduledExecutor = scheduledExecutor,
threadFactory = threadFactory,
)
}

/** Similar to [[fromConfigCustomEc]], but with a separate effect for the construction of the Transactor
*
* @tparam M0 the effect to construct the [[HikariTransactor]] in
* @tparam M the effect under which the [[HikariTransactor]] runs
*/
def fromConfigCustomEcSeparateEffect[M0[_] : Sync, M[_] : Async](
config: Config,
connectEC: ExecutionContext,
logHandler: Option[LogHandler[M]] = None,
Expand All @@ -70,27 +113,57 @@ object HikariTransactor {
threadFactory = threadFactory
)
)
.flatMap(fromHikariConfigCustomEc[M0, M](_, connectEC, logHandler))
.flatMap(fromHikariConfigCustomEcSeparateEffect[M0, M](_, connectEC, logHandler))
}

/** Resource yielding a new `HikariTransactor` configured with the given Config.
* The `connectEC` is created automatically, with the same size as the Hikari pool.
*
* @tparam M0 the effect to create a [[HikariTransactor]]
* @tparam M the effect under which the [[HikariTransactor]] runs
*/
def fromConfig[M0[_]: Sync, M[_]: Async](
config: Config,
logHandler: Option[LogHandler[M]] = None,
dataSource: Option[DataSource] = None,
dataSourceProperties: Option[Properties] = None,
healthCheckProperties: Option[Properties] = None,
healthCheckRegistry: Option[Object] = None,
metricRegistry: Option[Object] = None,
metricsTrackerFactory: Option[MetricsTrackerFactory] = None,
scheduledExecutor: Option[ScheduledExecutorService] = None,
threadFactory: Option[ThreadFactory] = None,
): Resource[M0, HikariTransactor[M]] = {
def fromConfig[M[_] : Async](
config: Config,
logHandler: Option[LogHandler[M]] = None,
dataSource: Option[DataSource] = None,
dataSourceProperties: Option[Properties] = None,
healthCheckProperties: Option[Properties] = None,
healthCheckRegistry: Option[Object] = None,
metricRegistry: Option[Object] = None,
metricsTrackerFactory: Option[MetricsTrackerFactory] = None,
scheduledExecutor: Option[ScheduledExecutorService] = None,
threadFactory: Option[ThreadFactory] = None,
): Resource[M, HikariTransactor[M]] = {
fromConfigSeparateEffect[M, M](
config = config,
logHandler = logHandler,
dataSource = dataSource,
dataSourceProperties = dataSourceProperties,
healthCheckProperties = healthCheckProperties,
healthCheckRegistry = healthCheckRegistry,
metricRegistry = metricRegistry,
metricsTrackerFactory = metricsTrackerFactory,
scheduledExecutor = scheduledExecutor,
threadFactory = threadFactory,
)
}

/** Similar to [[fromConfig]], but with a separate effect for the construction of the Transactor
*
* @tparam M0 the effect to construct the [[HikariTransactor]] in
* @tparam M the effect under which the [[HikariTransactor]] runs
*/
def fromConfigSeparateEffect[M0[_] : Sync, M[_] : Async](
config: Config,
logHandler: Option[LogHandler[M]] = None,
dataSource: Option[DataSource] = None,
dataSourceProperties: Option[Properties] = None,
healthCheckProperties: Option[Properties] = None,
healthCheckRegistry: Option[Object] = None,
metricRegistry: Option[Object] = None,
metricsTrackerFactory: Option[MetricsTrackerFactory] = None,
scheduledExecutor: Option[ScheduledExecutorService] = None,
threadFactory: Option[ThreadFactory] = None,
): Resource[M0, HikariTransactor[M]] = {
Resource
.liftK(
Config.makeHikariConfig[M0](
Expand All @@ -105,13 +178,25 @@ object HikariTransactor {
threadFactory = threadFactory
)
)
.flatMap(fromHikariConfig[M0, M](_, logHandler))
.flatMap(fromHikariConfigSeparateEffect[M0, M](_, logHandler))
}

/** Resource yielding a new `HikariTransactor` configured with the given HikariConfig.
* Unless you have a good reason, consider using [[fromHikariConfig]], it will be created automatically for you.
*/
def fromHikariConfigCustomEc[M0[_]: Sync, M[_]: Async](
def fromHikariConfigCustomEc[M[_] : Async](
hikariConfig: HikariConfig,
connectEC: ExecutionContext,
logHandler: Option[LogHandler[M]] = None
): Resource[M, HikariTransactor[M]] =
fromHikariConfigCustomEcSeparateEffect[M, M](hikariConfig, connectEC, logHandler)

/** Similar to [[fromHikariConfigCustomEc]], but with a separate effect for the construction of the Transactor
*
* @tparam M0 the effect to construct the [[HikariTransactor]] in
* @tparam M the effect under which the [[HikariTransactor]] runs
*/
def fromHikariConfigCustomEcSeparateEffect[M0[_] : Sync, M[_] : Async](
hikariConfig: HikariConfig,
connectEC: ExecutionContext,
logHandler: Option[LogHandler[M]] = None
Expand All @@ -122,44 +207,55 @@ object HikariTransactor {
/** Resource yielding a new `HikariTransactor` configured with the given HikariConfig.
* The connection ExecutionContext (used for waiting for a connection from the connection pool) is created automatically, with the same size as the Hikari connection pool.
*/
def fromHikariConfig[M0[_]: Sync, M[_]: Async](
def fromHikariConfig[M[_] : Async](
hikariConfig: HikariConfig,
logHandler: Option[LogHandler[M]] = None
): Resource[M, HikariTransactor[M]] =
fromHikariConfigSeparateEffect[M, M](hikariConfig, logHandler)

/** Similar to [[fromHikariConfig]], but with a separate effect for the construction of the Transactor
*
* @tparam M0 the effect to construct the [[HikariTransactor]] in
* @tparam M the effect under which the [[HikariTransactor]] runs
*/
def fromHikariConfigSeparateEffect[M0[_] : Sync, M[_] : Async](
hikariConfig: HikariConfig,
logHandler: Option[LogHandler[M]] = None
): Resource[M0, HikariTransactor[M]] =
for {
// to populate unset fields with default values, like `maximumPoolSize`
_ <- Sync[M0].delay(hikariConfig.validate()).toResource
// Note that the number of JDBC connections is usually limited by the underlying JDBC pool.
// You may therefore want to limit your connection pool to the same size as the underlying JDBC pool
// as any additional threads are guaranteed to be blocked.
// https://tpolecat.github.io/doobie/docs/14-Managing-Connections.html#about-threading
connectEC <- ExecutionContexts.fixedThreadPool[M0](hikariConfig.getMaximumPoolSize)
result <- fromHikariConfigCustomEc[M0, M](hikariConfig, connectEC, logHandler)
} yield result
for {
// to populate unset fields with default values, like `maximumPoolSize`
_ <- Sync[M0].delay(hikariConfig.validate()).toResource
// Note that the number of JDBC connections is usually limited by the underlying JDBC pool.
// You may therefore want to limit your connection pool to the same size as the underlying JDBC pool
// as any additional threads are guaranteed to be blocked.
// https://tpolecat.github.io/doobie/docs/14-Managing-Connections.html#about-threading
connectEC <- ExecutionContexts.fixedThreadPool[M0](hikariConfig.getMaximumPoolSize)
result <- fromHikariConfigCustomEcSeparateEffect[M0, M](hikariConfig, connectEC, logHandler)
} yield result

/** Resource yielding a new `HikariTransactor` configured with the given info.
* Consider using `fromConfig` for better configurability.
*/
def newHikariTransactor[M[_]: Async](
def newHikariTransactor[M[_] : Async](
driverClassName: String,
url: String,
user: String,
pass: String,
connectEC: ExecutionContext,
url: String,
user: String,
pass: String,
connectEC: ExecutionContext,
logHandler: Option[LogHandler[M]] = None
): Resource[M, HikariTransactor[M]] =
for {
_ <- Resource.eval(Sync[M].delay(Class.forName(driverClassName)))
t <- initial[M, M](connectEC, logHandler)
t <- initial[M](connectEC, logHandler)
_ <- Resource.eval[M, Unit] {
t.configure { ds =>
Sync[M].delay[Unit] {
ds setJdbcUrl url
ds setUsername user
ds setPassword pass
}
}
t.configure { ds =>
Sync[M].delay[Unit] {
ds setJdbcUrl url
ds setUsername user
ds setPassword pass
}
}
}
} yield t

}

0 comments on commit b3377af

Please sign in to comment.