Permalink
Browse files

Default maxConnections == numThreads

See discussion in #1614 about deadlocks caused by maxConnections > numThreads.
  • Loading branch information...
hvesalai committed Mar 1, 2018
1 parent 56a99ed commit c79d50c55747515314f26b5f4749bb2b22084fe0
@@ -45,7 +45,7 @@ object HikariCPJdbcDataSource extends JdbcDataSourceFactory {
c.getStringOpt("connectionTestQuery").foreach(hconf.setConnectionTestQuery)
c.getStringOpt("connectionInitSql").foreach(hconf.setConnectionInitSql)
val numThreads = c.getIntOr("numThreads", 20)
hconf.setMaximumPoolSize(c.getIntOr("maxConnections", numThreads * 5))
hconf.setMaximumPoolSize(c.getIntOr("maxConnections", numThreads))
hconf.setMinimumIdle(c.getIntOr("minConnections", numThreads))
hconf.setPoolName(c.getStringOr("poolName", name))
hconf.setRegisterMbeans(c.getBooleanOr("registerMbeans", false))
@@ -35,7 +35,7 @@ class DataSourceTest {
MockDriver.reset
val db = JdbcBackend.Database.forConfig("databaseUrl")
try {
assertEquals(Some(100), db.source.maxConnections)
assertEquals(Some(20), db.source.maxConnections)
try Await.result(db.run(sqlu"dummy"), Duration.Inf) catch { case ex: SQLException => }
val (url, info) = MockDriver.getLast.getOrElse(fail("No connection data recorded").asInstanceOf[Nothing])
assertEquals("jdbc:postgresql://host/dbname", url)
@@ -50,7 +50,7 @@ class DataSourceTest {
MockDriver.reset
val db = JdbcBackend.Database.forConfig("databaseUrlNoPassword")
try {
assertEquals(Some(100), db.source.maxConnections)
assertEquals(Some(20), db.source.maxConnections)
try Await.result(db.run(sqlu"dummy"), Duration.Inf) catch { case ex: SQLException => }
val (url, info) = MockDriver.getLast.getOrElse(fail("No connection data recorded").asInstanceOf[Nothing])
assertEquals("jdbc:postgresql://host/dbname", url)
@@ -65,7 +65,7 @@ class DataSourceTest {
MockDriver.reset
val db = JdbcBackend.Database.forConfig("databaseUrlMySQL")
try {
assertEquals(Some(100), db.source.maxConnections)
assertEquals(Some(20), db.source.maxConnections)
try Await.result(db.run(sqlu"dummy"), Duration.Inf) catch { case ex: SQLException => }
val (url, info) = MockDriver.getLast.getOrElse(fail("No connection data recorded").asInstanceOf[Nothing])
assertEquals("jdbc:mysql://host/dbname?useUnicode=yes&characterEncoding=UTF-8&connectionCollation=utf8_general_ci", url)
@@ -80,7 +80,7 @@ class DataSourceTest {
MockDriver.reset
val db = JdbcBackend.Database.forConfig("databaseUrlMySQLNoPassword")
try {
assertEquals(Some(100), db.source.maxConnections)
assertEquals(Some(20), db.source.maxConnections)
try Await.result(db.run(sqlu"dummy"), Duration.Inf) catch { case ex: SQLException => }
val (url, info) = MockDriver.getLast.getOrElse(fail("No connection data recorded").asInstanceOf[Nothing])
assertEquals("jdbc:mysql://host/dbname?useUnicode=yes&characterEncoding=UTF-8&connectionCollation=utf8_general_ci", url)
@@ -95,7 +95,7 @@ class DataSourceTest {
MockDriver.reset
val db = JdbcBackend.Database.forConfig("altDatabaseUrl")
try {
assertEquals(Some(100), db.source.maxConnections)
assertEquals(Some(20), db.source.maxConnections)
try Await.result(db.run(sqlu"dummy"), Duration.Inf) catch { case ex: SQLException => }
val (url, info) = MockDriver.getLast.getOrElse(fail("No connection data recorded").asInstanceOf[Nothing])
assertEquals("jdbc:postgresql://host/dbname", url)
@@ -132,7 +132,7 @@ class DataSourceTest {
|""".stripMargin
))
try {
assertEquals("maxConnections should be numThreads * 5", Some(50), db.source.maxConnections)
assertEquals("maxConnections should be numThreads", Some(10), db.source.maxConnections)
} finally db.close
}
@@ -39,7 +39,7 @@ class MBeansTest {
Await.result(db.run(sqlu"""create alias sleep for "java.lang.Thread.sleep""""), Duration(10, TimeUnit.SECONDS))
assertEquals(1, mbeanServer.getAttribute(aeBeanName, "MaxThreads"))
mbeanServer.getAttribute(aeBeanName, "ActiveThreads") // we expect 0 but the returned number is only an estimate
mbeanServer.getAttribute(aeBeanName, "ActiveThreads") // we expect 1, since minThreads == maxThreads
assertEquals(1000, mbeanServer.getAttribute(aeBeanName, "MaxQueueSize"))
assertEquals(0, mbeanServer.getAttribute(aeBeanName, "QueueSize"))
@@ -47,23 +47,23 @@ class SlickDeadlockTest extends AsyncTest[JdbcTestDB] {
@Test def slickDoesNotDeadlock() {
val tasks = 1 to 100 map { i =>
val tasks = 1 to 51 map { i =>
val action = { testTable += i }
.flatMap { _ => testTable.length.result }
.flatMap { _ => DBIO.successful(s"inserted value $i") }
database.run(action.transactionally)
}
Await.result(Future.sequence(tasks), Duration(10, TimeUnit.SECONDS))
Await.result(Future.sequence(tasks), Duration(100, TimeUnit.SECONDS))
}
@Test def slickDoesNotDeadlockWithSleeps(): Unit = {
val tasks = 1 to 50 map { c =>
val tasks = 1 to 21 map { c =>
val action = sql"select $c".as[Int].head.map { i => Thread.sleep(if(c == 1) 100 else 200); i }
database.run(action.transactionally)
}
Await.result(Future.sequence(tasks), Duration(10, TimeUnit.SECONDS))
Await.result(Future.sequence(tasks), Duration(100, TimeUnit.SECONDS))
}
@@ -73,14 +73,14 @@ class SlickDeadlockTest extends AsyncTest[JdbcTestDB] {
(blobTable += (1, new SerialBlob(Array[Byte](1,2,3)))) >>
(blobTable += (2, new SerialBlob(Array[Byte](4,5)))) >>
blobTable.result
).transactionally), Duration(2, TimeUnit.SECONDS))
).transactionally), Duration(20, TimeUnit.SECONDS))
val tasks = 1 to 100 map { i =>
val tasks = 1 to 51 map { i =>
materializeAsync[(Int, Blob), (Int, String)](database.stream(blobTable.result.transactionally, bufferNext = false),
{ case (id, data) => database.io((id, data.getBytes(1, data.length.toInt).mkString)) })
}
Await.result(Future.sequence(tasks), Duration(10, TimeUnit.SECONDS))
Await.result(Future.sequence(tasks), Duration(100, TimeUnit.SECONDS))
}
}
@@ -186,7 +186,7 @@ trait JdbcBackend extends RelationalBackend {
* the JDBC driver. This is preferred over using `driver`. Note that `url` is ignored when
* this key is set (You have to use `properties` to configure the database
* connection instead).</li>
* <li>`maxConnections` (Int, optional, default: `numThreads` * 5): The maximum number of
* <li>`maxConnections` (Int, optional, default: `numThreads`): The maximum number of
* connections in the pool.</li>
* <li>`minConnections` (Int, optional, default: same as `numThreads`): The minimum number
* of connections to keep in the pool.</li>
@@ -287,7 +287,7 @@ trait JdbcBackend extends RelationalBackend {
val source = JdbcDataSource.forConfig(usedConfig, driver, path, classLoader)
val poolName = usedConfig.getStringOr("poolName", path)
val numThreads = usedConfig.getIntOr("numThreads", 20)
val maxConnections = source.maxConnections.fold(numThreads*5)(identity)
val maxConnections = source.maxConnections.fold(numThreads)(identity)
val registerMbeans = usedConfig.getBooleanOr("registerMbeans", false)
val executor = AsyncExecutor(poolName, numThreads, numThreads, usedConfig.getIntOr("queueSize", 1000),
maxConnections, registerMbeans = registerMbeans)

0 comments on commit c79d50c

Please sign in to comment.