From a3d1e277258eac1b8f26b6e7bcd5d41dc44b98fa Mon Sep 17 00:00:00 2001 From: Serhii Dashko Date: Mon, 30 May 2022 21:21:25 +0300 Subject: [PATCH 1/2] Adds HikariCP (#639) --- build.sbt | 20 +++- .../scala/zio/sql/HikariConnectionPool.scala | 34 ++++++ .../zio/sql/HikariConnectionPoolConfig.scala | 53 +++++++++ .../zio/sql/HikariConnectionPoolSpec.scala | 102 ++++++++++++++++++ .../scala/zio/sql/MySqlTestContainer.scala | 20 ++++ 5 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 jdbc-hikaricp/src/main/scala/zio/sql/HikariConnectionPool.scala create mode 100644 jdbc-hikaricp/src/main/scala/zio/sql/HikariConnectionPoolConfig.scala create mode 100644 jdbc-hikaricp/src/test/scala/zio/sql/HikariConnectionPoolSpec.scala create mode 100644 jdbc-hikaricp/src/test/scala/zio/sql/MySqlTestContainer.scala diff --git a/build.sbt b/build.sbt index a321d57f0..a185948df 100644 --- a/build.sbt +++ b/build.sbt @@ -69,7 +69,8 @@ lazy val root = project mysql, oracle, postgres, - sqlserver + sqlserver, + jdbc_hikaricp ) lazy val core = crossProject(JSPlatform, JVMPlatform) @@ -155,6 +156,23 @@ lazy val jdbc = project .settings(testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework")) .dependsOn(core.jvm) +lazy val jdbc_hikaricp = project + .in(file("jdbc-hikaricp")) + .settings(stdSettings("zio-sql-jdbc-hickaricp")) + .settings(buildInfoSettings("zio.sql.jdbc-hickaricp")) + .settings( + libraryDependencies ++= Seq( + "com.zaxxer" % "HikariCP" % "5.0.1", + "dev.zio" %% "zio-test" % zioVersion % Test, + "dev.zio" %% "zio-test-sbt" % zioVersion % Test, + "org.testcontainers" % "mysql" % testcontainersVersion % Test, + "mysql" % "mysql-connector-java" % "8.0.29" % Test, + "com.dimafeng" %% "testcontainers-scala-mysql" % testcontainersScalaVersion % Test + ) + ) + .settings(testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework")) + .dependsOn(jdbc) + lazy val mysql = project .in(file("mysql")) .dependsOn(jdbc % "compile->compile;test->test") diff --git a/jdbc-hikaricp/src/main/scala/zio/sql/HikariConnectionPool.scala b/jdbc-hikaricp/src/main/scala/zio/sql/HikariConnectionPool.scala new file mode 100644 index 000000000..8696eb1f9 --- /dev/null +++ b/jdbc-hikaricp/src/main/scala/zio/sql/HikariConnectionPool.scala @@ -0,0 +1,34 @@ +package zio.sql +import com.zaxxer.hikari.{HikariConfig, HikariDataSource} +import zio.{Scope, ZIO, ZLayer} + +import java.sql.{Connection, SQLException} + +class HikariConnectionPool private (hikariDataSource: HikariDataSource) extends ConnectionPool { + + private[sql] val dataSource = hikariDataSource + + /** + * Retrieves a JDBC java.sql.Connection as a [[ZIO[Scope, Exception, Connection]]] resource. + * The managed resource will safely acquire and release the connection, and + * may be interrupted or timed out if necessary. + */ + override def connection: ZIO[Scope, Exception, Connection] = { + ZIO.acquireRelease(ZIO.attemptBlocking(hikariDataSource.getConnection).refineToOrDie[SQLException])(con => ZIO.attemptBlocking(hikariDataSource.evictConnection(con)).orDie) + } +} + +object HikariConnectionPool { + + private[sql] def initDataSource(config: HikariConfig): ZIO[Scope, Throwable, HikariDataSource] = + ZIO.acquireRelease(ZIO.attemptBlocking(new HikariDataSource(config)))(ds => ZIO.attemptBlocking(ds.close()).orDie) + + val live: ZLayer[HikariConnectionPoolConfig, Throwable, HikariConnectionPool] = + ZLayer.scoped { + for { + config <- ZIO.service[HikariConnectionPoolConfig] + dataSource <- initDataSource(config.toHikariConfig) + pool = new HikariConnectionPool(dataSource) + } yield pool + } +} diff --git a/jdbc-hikaricp/src/main/scala/zio/sql/HikariConnectionPoolConfig.scala b/jdbc-hikaricp/src/main/scala/zio/sql/HikariConnectionPoolConfig.scala new file mode 100644 index 000000000..f788a9028 --- /dev/null +++ b/jdbc-hikaricp/src/main/scala/zio/sql/HikariConnectionPoolConfig.scala @@ -0,0 +1,53 @@ +package zio.sql + +import com.zaxxer.hikari.HikariConfig + + +/** + * Configuration information for the connection pool. + * + * @param url The JDBC connection string. + * @param properties JDBC connection properties (username / password could go here). + * @param poolSize The size of the pool. + * @param connectionTimeout Maximum number of milliseconds that a client will wait for a connection from the pool. + * If this time is exceeded without a connection becoming available, a SQLException will be thrown from javax.sql.DataSource.getConnection(). + * @param idleTimeout This property controls the maximum amount of time (in milliseconds) that a connection is allowed to sit idle in the pool. + * Whether a connection is retired as idle or not is subject to a maximum variation of +30 seconds, and average variation of +15 seconds. + * A connection will never be retired as idle before this timeout. A value of 0 means that idle connections are never removed from the pool. + * @param initializationFailTimeout the number of milliseconds before the + * pool initialization fails, or 0 to validate connection setup but continue with + * pool start, or less than zero to skip all initialization checks and start the + * pool without delay. + * @param maxLifetime This property controls the maximum lifetime of a connection in the pool. + * When a connection reaches this timeout, even if recently used, it will be retired from the pool. + * An in-use connection will never be retired, only when it is idle will it be removed. Should be bigger then 30000 + * @param minimumIdle The property controls the minimum number of idle connections that HikariCP tries to maintain in the pool, including both idle and in-use connections. + * If the idle connections dip below this value, HikariCP will make a best effort to restore them quickly and efficiently. + */ +final case class HikariConnectionPoolConfig( + url: String, + userName: String, + password: String, + poolSize: Int = 10, + autoCommit: Boolean = true, + connectionTimeout: Option[Long] = None, + idleTimeout: Option[Long] = None, + initializationFailTimeout: Option[Long] = None, + maxLifetime: Option[Long] = None, + minimumIdle: Option[Int] = None + ) { + private[sql] def toHikariConfig = { + val hikariConfig = new HikariConfig() + hikariConfig.setJdbcUrl(this.url) + hikariConfig.setAutoCommit(this.autoCommit) + hikariConfig.setMaximumPoolSize(this.poolSize) + hikariConfig.setUsername(userName) + hikariConfig.setPassword(password) + connectionTimeout.foreach(hikariConfig.setConnectionTimeout) + idleTimeout.foreach(hikariConfig.setIdleTimeout) + initializationFailTimeout.foreach(hikariConfig.setInitializationFailTimeout) + maxLifetime.foreach(hikariConfig.setMaxLifetime) + minimumIdle.foreach(hikariConfig.setMinimumIdle) + hikariConfig + } +} diff --git a/jdbc-hikaricp/src/test/scala/zio/sql/HikariConnectionPoolSpec.scala b/jdbc-hikaricp/src/test/scala/zio/sql/HikariConnectionPoolSpec.scala new file mode 100644 index 000000000..8f3d40b3e --- /dev/null +++ b/jdbc-hikaricp/src/test/scala/zio/sql/HikariConnectionPoolSpec.scala @@ -0,0 +1,102 @@ +package zio.sql + +import zio.test.TestAspect.{sequential, timeout, withLiveClock} +import zio.test.{TestEnvironment, _} +import zio.{ZIO, ZLayer, durationInt} + + +object HikariConnectionPoolSpec extends ZIOSpecDefault { + + + val mySqlConfigLayer: ZLayer[Any, Throwable, MySqlConfig] = + ZLayer.scoped { + MySqlTestContainer.mysql() + .map(a => + MySqlConfig( + url = a.jdbcUrl, + username = a.username, + password = a.password + ) + ) + } + + val hikariPoolConfigLayer: ZLayer[MySqlConfig, Nothing, HikariConnectionPoolConfig] = ZLayer.fromFunction((conf: MySqlConfig) => HikariConnectionPoolConfig(url = conf.url, userName = conf.username, password = conf.password)) + val poolLayer: ZLayer[HikariConnectionPoolConfig, Nothing, HikariConnectionPool] = HikariConnectionPool.live.orDie + + override def spec: Spec[TestEnvironment, Any] = + specLayered.provideCustomShared(mySqlConfigLayer.orDie) + + def specLayered: Spec[TestEnvironment with MySqlConfig, Any] = + suite("Hikaricp module")( + test("Pool size should be configurable") { + val poolSize = 20 + (for { + cp <- ZIO.service[HikariConnectionPool] + } yield assertTrue(cp.dataSource.getMaximumPoolSize == poolSize)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer.map(_.update(_.copy(poolSize = poolSize))) >>> poolLayer) + } @@ timeout(10.seconds) @@ withLiveClock, + + test("Pool size should have 10 connections by default") { + (for { + cp <- ZIO.service[HikariConnectionPool] + _ <- ZIO.replicateZIO(10)(ZIO.scoped(cp.connection)) + } yield assertTrue(cp.dataSource.getMaximumPoolSize == 10)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer >>> poolLayer) + } @@ timeout(10.minutes) @@ withLiveClock, + + test("It should be possible to acquire connections from the pool") { + val poolSize = 20 + (for { + cp <- ZIO.service[HikariConnectionPool] + _ <- ZIO.collectAllParDiscard(ZIO.replicate(poolSize)(ZIO.scoped(cp.connection *> ZIO.sleep(500.millisecond)))) + } yield assert("")(Assertion.anything)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer.map(_.update(_.copy(poolSize = poolSize))) >>> poolLayer) + } @@ timeout(10.seconds) @@ withLiveClock, + + test("Auto commit should be configurable") { + val autoCommit = false + (for { + cp <- ZIO.service[HikariConnectionPool] + } yield assertTrue(cp.dataSource.isAutoCommit == autoCommit)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer.map(_.update(_.copy(autoCommit = autoCommit))) >>> poolLayer) + } @@ timeout(10.seconds) @@ withLiveClock, + + test("Auto commit should be true by default") { + (for { + cp <- ZIO.service[HikariConnectionPool] + } yield assertTrue(cp.dataSource.isAutoCommit)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer >>> poolLayer) + } @@ timeout(10.seconds) @@ withLiveClock, + + test("Connection timeout should be configurable") { + val connectionTimeout = 2000L + (for { + cp <- ZIO.service[HikariConnectionPool] + } yield assertTrue(cp.dataSource.getConnectionTimeout == connectionTimeout)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer.map(_.update(_.copy(connectionTimeout = Some(connectionTimeout)))) >>> poolLayer) + } @@ timeout(10.seconds) @@ withLiveClock, + + test("Idle timeout should be configurable") { + val idleTimeout = 2000L + (for { + cp <- ZIO.service[HikariConnectionPool] + } yield assertTrue(cp.dataSource.getIdleTimeout == idleTimeout)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer.map(_.update(_.copy(idleTimeout = Some(idleTimeout)))) >>> poolLayer) + } @@ timeout(10.seconds) @@ withLiveClock, + + test("initialization fail timeout should be configurable") { + val initializationFailTimeout = 2000L + (for { + cp <- ZIO.service[HikariConnectionPool] + } yield assertTrue(cp.dataSource.getInitializationFailTimeout == initializationFailTimeout)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer.map(_.update(_.copy(initializationFailTimeout = Some(initializationFailTimeout)))) >>> poolLayer) + } @@ timeout(10.seconds) @@ withLiveClock, + + test("max lifetime should be configurable") { + val maxLifetime = 40000L + (for { + cp <- ZIO.service[HikariConnectionPool] + } yield assertTrue(cp.dataSource.getMaxLifetime == maxLifetime)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer.map(_.update(_.copy(maxLifetime = Some(maxLifetime)))) >>> poolLayer) + } @@ timeout(10.seconds) @@ withLiveClock, + + test("minimum idle should be configurable") { + val minimumIdle = 2 + (for { + cp <- ZIO.service[HikariConnectionPool] + } yield assertTrue(cp.dataSource.getMinimumIdle == minimumIdle)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer.map(_.update(_.copy(minimumIdle = Some(minimumIdle)))) >>> poolLayer) + } @@ timeout(10.seconds) @@ withLiveClock, + + ) @@ sequential +} diff --git a/jdbc-hikaricp/src/test/scala/zio/sql/MySqlTestContainer.scala b/jdbc-hikaricp/src/test/scala/zio/sql/MySqlTestContainer.scala new file mode 100644 index 000000000..ebba6fa31 --- /dev/null +++ b/jdbc-hikaricp/src/test/scala/zio/sql/MySqlTestContainer.scala @@ -0,0 +1,20 @@ +package zio.sql + +import com.dimafeng.testcontainers.MySQLContainer +import org.testcontainers.utility.DockerImageName +import zio._ + +final case class MySqlConfig(username: String, password: String, url: String) +object MySqlTestContainer { + + def mysql(imageName: String = "mysql"): ZIO[Scope, Throwable, MySQLContainer] = + ZIO.acquireRelease { + ZIO.attemptBlocking { + val c = new MySQLContainer( + mysqlImageVersion = Option(imageName).map(DockerImageName.parse) + ) + c.start() + c + } + }(container => ZIO.attemptBlocking(container.stop()).orDie) +} From 9658c654f1da7a0db8d15640d9d98b9efb336e14 Mon Sep 17 00:00:00 2001 From: Serhii Dashko Date: Tue, 31 May 2022 12:24:56 +0300 Subject: [PATCH 2/2] Adds option for connectionInitSql (#639) --- .../scala/zio/sql/HikariConnectionPool.scala | 17 ++-- .../zio/sql/HikariConnectionPoolConfig.scala | 29 +++--- .../zio/sql/HikariConnectionPoolSpec.scala | 89 ++++++++++++------- .../scala/zio/sql/MySqlTestContainer.scala | 5 +- 4 files changed, 89 insertions(+), 51 deletions(-) diff --git a/jdbc-hikaricp/src/main/scala/zio/sql/HikariConnectionPool.scala b/jdbc-hikaricp/src/main/scala/zio/sql/HikariConnectionPool.scala index 8696eb1f9..b72a28464 100644 --- a/jdbc-hikaricp/src/main/scala/zio/sql/HikariConnectionPool.scala +++ b/jdbc-hikaricp/src/main/scala/zio/sql/HikariConnectionPool.scala @@ -1,8 +1,8 @@ package zio.sql -import com.zaxxer.hikari.{HikariConfig, HikariDataSource} -import zio.{Scope, ZIO, ZLayer} +import com.zaxxer.hikari.{ HikariConfig, HikariDataSource } +import zio.{ Scope, ZIO, ZLayer } -import java.sql.{Connection, SQLException} +import java.sql.{ Connection, SQLException } class HikariConnectionPool private (hikariDataSource: HikariDataSource) extends ConnectionPool { @@ -13,9 +13,10 @@ class HikariConnectionPool private (hikariDataSource: HikariDataSource) extends * The managed resource will safely acquire and release the connection, and * may be interrupted or timed out if necessary. */ - override def connection: ZIO[Scope, Exception, Connection] = { - ZIO.acquireRelease(ZIO.attemptBlocking(hikariDataSource.getConnection).refineToOrDie[SQLException])(con => ZIO.attemptBlocking(hikariDataSource.evictConnection(con)).orDie) - } + override def connection: ZIO[Scope, Exception, Connection] = + ZIO.acquireRelease(ZIO.attemptBlocking(hikariDataSource.getConnection).refineToOrDie[SQLException])(con => + ZIO.attemptBlocking(hikariDataSource.evictConnection(con)).orDie + ) } object HikariConnectionPool { @@ -26,9 +27,9 @@ object HikariConnectionPool { val live: ZLayer[HikariConnectionPoolConfig, Throwable, HikariConnectionPool] = ZLayer.scoped { for { - config <- ZIO.service[HikariConnectionPoolConfig] + config <- ZIO.service[HikariConnectionPoolConfig] dataSource <- initDataSource(config.toHikariConfig) - pool = new HikariConnectionPool(dataSource) + pool = new HikariConnectionPool(dataSource) } yield pool } } diff --git a/jdbc-hikaricp/src/main/scala/zio/sql/HikariConnectionPoolConfig.scala b/jdbc-hikaricp/src/main/scala/zio/sql/HikariConnectionPoolConfig.scala index f788a9028..45ab36908 100644 --- a/jdbc-hikaricp/src/main/scala/zio/sql/HikariConnectionPoolConfig.scala +++ b/jdbc-hikaricp/src/main/scala/zio/sql/HikariConnectionPoolConfig.scala @@ -2,7 +2,6 @@ package zio.sql import com.zaxxer.hikari.HikariConfig - /** * Configuration information for the connection pool. * @@ -23,19 +22,24 @@ import com.zaxxer.hikari.HikariConfig * An in-use connection will never be retired, only when it is idle will it be removed. Should be bigger then 30000 * @param minimumIdle The property controls the minimum number of idle connections that HikariCP tries to maintain in the pool, including both idle and in-use connections. * If the idle connections dip below this value, HikariCP will make a best effort to restore them quickly and efficiently. + * @param connectionInitSql the SQL to execute on new connections + * Set the SQL string that will be executed on all new connections when they are + * created, before they are added to the pool. If this query fails, it will be + * treated as a failed connection attempt. */ final case class HikariConnectionPoolConfig( - url: String, - userName: String, - password: String, - poolSize: Int = 10, - autoCommit: Boolean = true, - connectionTimeout: Option[Long] = None, - idleTimeout: Option[Long] = None, - initializationFailTimeout: Option[Long] = None, - maxLifetime: Option[Long] = None, - minimumIdle: Option[Int] = None - ) { + url: String, + userName: String, + password: String, + poolSize: Int = 10, + autoCommit: Boolean = true, + connectionTimeout: Option[Long] = None, + idleTimeout: Option[Long] = None, + initializationFailTimeout: Option[Long] = None, + maxLifetime: Option[Long] = None, + minimumIdle: Option[Int] = None, + connectionInitSql: Option[String] = None +) { private[sql] def toHikariConfig = { val hikariConfig = new HikariConfig() hikariConfig.setJdbcUrl(this.url) @@ -48,6 +52,7 @@ final case class HikariConnectionPoolConfig( initializationFailTimeout.foreach(hikariConfig.setInitializationFailTimeout) maxLifetime.foreach(hikariConfig.setMaxLifetime) minimumIdle.foreach(hikariConfig.setMinimumIdle) + connectionInitSql.foreach(hikariConfig.setConnectionInitSql) hikariConfig } } diff --git a/jdbc-hikaricp/src/test/scala/zio/sql/HikariConnectionPoolSpec.scala b/jdbc-hikaricp/src/test/scala/zio/sql/HikariConnectionPoolSpec.scala index 8f3d40b3e..31a2a5f5b 100644 --- a/jdbc-hikaricp/src/test/scala/zio/sql/HikariConnectionPoolSpec.scala +++ b/jdbc-hikaricp/src/test/scala/zio/sql/HikariConnectionPoolSpec.scala @@ -1,16 +1,15 @@ package zio.sql -import zio.test.TestAspect.{sequential, timeout, withLiveClock} -import zio.test.{TestEnvironment, _} -import zio.{ZIO, ZLayer, durationInt} - +import zio.test.TestAspect.{ sequential, timeout, withLiveClock } +import zio.test.{ TestEnvironment, _ } +import zio.{ durationInt, ZIO, ZLayer } object HikariConnectionPoolSpec extends ZIOSpecDefault { - val mySqlConfigLayer: ZLayer[Any, Throwable, MySqlConfig] = ZLayer.scoped { - MySqlTestContainer.mysql() + MySqlTestContainer + .mysql() .map(a => MySqlConfig( url = a.jdbcUrl, @@ -20,8 +19,11 @@ object HikariConnectionPoolSpec extends ZIOSpecDefault { ) } - val hikariPoolConfigLayer: ZLayer[MySqlConfig, Nothing, HikariConnectionPoolConfig] = ZLayer.fromFunction((conf: MySqlConfig) => HikariConnectionPoolConfig(url = conf.url, userName = conf.username, password = conf.password)) - val poolLayer: ZLayer[HikariConnectionPoolConfig, Nothing, HikariConnectionPool] = HikariConnectionPool.live.orDie + val hikariPoolConfigLayer: ZLayer[MySqlConfig, Nothing, HikariConnectionPoolConfig] = + ZLayer.fromFunction((conf: MySqlConfig) => + HikariConnectionPoolConfig(url = conf.url, userName = conf.username, password = conf.password) + ) + val poolLayer: ZLayer[HikariConnectionPoolConfig, Nothing, HikariConnectionPool] = HikariConnectionPool.live.orDie override def spec: Spec[TestEnvironment, Any] = specLayered.provideCustomShared(mySqlConfigLayer.orDie) @@ -29,74 +31,101 @@ object HikariConnectionPoolSpec extends ZIOSpecDefault { def specLayered: Spec[TestEnvironment with MySqlConfig, Any] = suite("Hikaricp module")( test("Pool size should be configurable") { - val poolSize = 20 + val poolSize = 20 (for { cp <- ZIO.service[HikariConnectionPool] - } yield assertTrue(cp.dataSource.getMaximumPoolSize == poolSize)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer.map(_.update(_.copy(poolSize = poolSize))) >>> poolLayer) + } yield assertTrue(cp.dataSource.getMaximumPoolSize == poolSize)) + .provideSomeLayer[TestEnvironment with MySqlConfig]( + hikariPoolConfigLayer.map(_.update(_.copy(poolSize = poolSize))) >>> poolLayer + ) } @@ timeout(10.seconds) @@ withLiveClock, - test("Pool size should have 10 connections by default") { (for { cp <- ZIO.service[HikariConnectionPool] _ <- ZIO.replicateZIO(10)(ZIO.scoped(cp.connection)) - } yield assertTrue(cp.dataSource.getMaximumPoolSize == 10)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer >>> poolLayer) + } yield assertTrue(cp.dataSource.getMaximumPoolSize == 10)) + .provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer >>> poolLayer) } @@ timeout(10.minutes) @@ withLiveClock, - test("It should be possible to acquire connections from the pool") { val poolSize = 20 (for { cp <- ZIO.service[HikariConnectionPool] - _ <- ZIO.collectAllParDiscard(ZIO.replicate(poolSize)(ZIO.scoped(cp.connection *> ZIO.sleep(500.millisecond)))) - } yield assert("")(Assertion.anything)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer.map(_.update(_.copy(poolSize = poolSize))) >>> poolLayer) + _ <- + ZIO.collectAllParDiscard(ZIO.replicate(poolSize)(ZIO.scoped(cp.connection *> ZIO.sleep(500.millisecond)))) + } yield assert("")(Assertion.anything)).provideSomeLayer[TestEnvironment with MySqlConfig]( + hikariPoolConfigLayer.map(_.update(_.copy(poolSize = poolSize))) >>> poolLayer + ) } @@ timeout(10.seconds) @@ withLiveClock, - test("Auto commit should be configurable") { val autoCommit = false (for { cp <- ZIO.service[HikariConnectionPool] - } yield assertTrue(cp.dataSource.isAutoCommit == autoCommit)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer.map(_.update(_.copy(autoCommit = autoCommit))) >>> poolLayer) + } yield assertTrue(cp.dataSource.isAutoCommit == autoCommit)) + .provideSomeLayer[TestEnvironment with MySqlConfig]( + hikariPoolConfigLayer.map(_.update(_.copy(autoCommit = autoCommit))) >>> poolLayer + ) } @@ timeout(10.seconds) @@ withLiveClock, - test("Auto commit should be true by default") { (for { cp <- ZIO.service[HikariConnectionPool] - } yield assertTrue(cp.dataSource.isAutoCommit)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer >>> poolLayer) + } yield assertTrue(cp.dataSource.isAutoCommit)) + .provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer >>> poolLayer) } @@ timeout(10.seconds) @@ withLiveClock, - test("Connection timeout should be configurable") { val connectionTimeout = 2000L (for { cp <- ZIO.service[HikariConnectionPool] - } yield assertTrue(cp.dataSource.getConnectionTimeout == connectionTimeout)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer.map(_.update(_.copy(connectionTimeout = Some(connectionTimeout)))) >>> poolLayer) + } yield assertTrue(cp.dataSource.getConnectionTimeout == connectionTimeout)) + .provideSomeLayer[TestEnvironment with MySqlConfig]( + hikariPoolConfigLayer.map(_.update(_.copy(connectionTimeout = Some(connectionTimeout)))) >>> poolLayer + ) } @@ timeout(10.seconds) @@ withLiveClock, - test("Idle timeout should be configurable") { val idleTimeout = 2000L (for { cp <- ZIO.service[HikariConnectionPool] - } yield assertTrue(cp.dataSource.getIdleTimeout == idleTimeout)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer.map(_.update(_.copy(idleTimeout = Some(idleTimeout)))) >>> poolLayer) + } yield assertTrue(cp.dataSource.getIdleTimeout == idleTimeout)) + .provideSomeLayer[TestEnvironment with MySqlConfig]( + hikariPoolConfigLayer.map(_.update(_.copy(idleTimeout = Some(idleTimeout)))) >>> poolLayer + ) } @@ timeout(10.seconds) @@ withLiveClock, - test("initialization fail timeout should be configurable") { val initializationFailTimeout = 2000L (for { cp <- ZIO.service[HikariConnectionPool] - } yield assertTrue(cp.dataSource.getInitializationFailTimeout == initializationFailTimeout)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer.map(_.update(_.copy(initializationFailTimeout = Some(initializationFailTimeout)))) >>> poolLayer) + } yield assertTrue(cp.dataSource.getInitializationFailTimeout == initializationFailTimeout)) + .provideSomeLayer[TestEnvironment with MySqlConfig]( + hikariPoolConfigLayer.map( + _.update(_.copy(initializationFailTimeout = Some(initializationFailTimeout))) + ) >>> poolLayer + ) } @@ timeout(10.seconds) @@ withLiveClock, - test("max lifetime should be configurable") { val maxLifetime = 40000L (for { cp <- ZIO.service[HikariConnectionPool] - } yield assertTrue(cp.dataSource.getMaxLifetime == maxLifetime)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer.map(_.update(_.copy(maxLifetime = Some(maxLifetime)))) >>> poolLayer) + } yield assertTrue(cp.dataSource.getMaxLifetime == maxLifetime)) + .provideSomeLayer[TestEnvironment with MySqlConfig]( + hikariPoolConfigLayer.map(_.update(_.copy(maxLifetime = Some(maxLifetime)))) >>> poolLayer + ) } @@ timeout(10.seconds) @@ withLiveClock, - test("minimum idle should be configurable") { val minimumIdle = 2 (for { cp <- ZIO.service[HikariConnectionPool] - } yield assertTrue(cp.dataSource.getMinimumIdle == minimumIdle)).provideSomeLayer[TestEnvironment with MySqlConfig](hikariPoolConfigLayer.map(_.update(_.copy(minimumIdle = Some(minimumIdle)))) >>> poolLayer) + } yield assertTrue(cp.dataSource.getMinimumIdle == minimumIdle)) + .provideSomeLayer[TestEnvironment with MySqlConfig]( + hikariPoolConfigLayer.map(_.update(_.copy(minimumIdle = Some(minimumIdle)))) >>> poolLayer + ) } @@ timeout(10.seconds) @@ withLiveClock, - + test("connection init SQL should be configurable") { + val initialSql = "SELECT 1 FROM test.test" + (for { + cp <- ZIO.service[HikariConnectionPool] + } yield assertTrue(cp.dataSource.getConnectionInitSql == initialSql)) + .provideSomeLayer[TestEnvironment with MySqlConfig]( + hikariPoolConfigLayer.map(_.update(_.copy(connectionInitSql = Some(initialSql)))) >>> poolLayer + ) + } @@ timeout(10.seconds) @@ withLiveClock ) @@ sequential } diff --git a/jdbc-hikaricp/src/test/scala/zio/sql/MySqlTestContainer.scala b/jdbc-hikaricp/src/test/scala/zio/sql/MySqlTestContainer.scala index ebba6fa31..16ec7cc17 100644 --- a/jdbc-hikaricp/src/test/scala/zio/sql/MySqlTestContainer.scala +++ b/jdbc-hikaricp/src/test/scala/zio/sql/MySqlTestContainer.scala @@ -12,7 +12,10 @@ object MySqlTestContainer { ZIO.attemptBlocking { val c = new MySQLContainer( mysqlImageVersion = Option(imageName).map(DockerImageName.parse) - ) + ).configure { a => + a.withInitScript("test_schema.sql") + () + } c.start() c }