slickでHikariCPを使った時の設定値の上書きに関して

slickでHikariCPを使うときに、設定値が上書きされて コードを見てたら気づいたことがあったので残します。

slickでHikariCPを使うときの設定

HikariCP https://github.com/brettwooldridge/HikariCP

db.master {
    url  = "jdbc:mysql://hoge:3306/hoge"
    driver=com.mysql.jdbc.Driver
    user = hoge
    password = hoge
    # connectionPoolはHikariCPを選択する部分
    connectionPool = HikariCP
    keepAliveConnection = true
    properties = {
         #HikariCPの設定ファイルをここに記入する
         maximumPoolSize = 10
         minimumIdle = 10
         ...etc
    }
}

こんな感じで設定してみたら、maximumPoolSizeminimumIdleが10になるので 動かすと、mysql側でshow processlistを確認したら20プロセスあった。

それで、設定の部分のコードを見てみました。

コネクションプールの選択の部分

https://github.com/slick/slick/blob/3.1/slick/src/main/scala/slick/jdbc/JdbcDataSource.scala

28行目あたりが、コネクションプールの選択の部分ですね。 connectionPoolの部分を見て、設定をしています。

def forConfig(c: Config, driver: Driver, name: String, classLoader: ClassLoader): JdbcDataSource = {
    def loadFactory(name: String): JdbcDataSourceFactory = {
      val clazz = classLoader.loadClass(name)
      clazz.getField("MODULE$").get(clazz).asInstanceOf[JdbcDataSourceFactory]
    }
    val pf: JdbcDataSourceFactory = c.getStringOr("connectionPool", "HikariCP") match {
      case "disabled" => DataSourceJdbcDataSource
      case "HikariCP" => loadFactory("slick.jdbc.hikaricp.HikariCPJdbcDataSource$")
      case "slick.jdbc.HikariCPJdbcDataSource" =>
        logger.warn("connectionPool class 'slick.jdbc.HikariCPJdbcDataSource$' has been renamed to 'slick.jdbc.hikaricp.HikariCPJdbcDataSource$'")
        loadFactory("slick.jdbc.hikaricp.HikariCPJdbcDataSource$")
      case name => loadFactory(name)
    }
    pf.forConfig(c, driver, name, classLoader)
  }

HikariCPの場合、slick.jdbc.hikaricp.HikariCPJdbcDataSource を見ていることがわかります。

HikariCPの設定の上書き

https://github.com/slick/slick/blob/3.1/slick-hikaricp/src/main/scala/slick/jdbc/hikaricp/HikariCPJdbcDataSource.scala

// propertiesの設定を読み込む
c.getPropertiesOpt("properties").foreach(hconf.setDataSourceProperties)

// HikariCPの設定を読み込む
hconf.setConnectionTimeout(c.getMillisecondsOr("connectionTimeout", 1000))
hconf.setValidationTimeout(c.getMillisecondsOr("validationTimeout", 1000))
hconf.setIdleTimeout(c.getMillisecondsOr("idleTimeout", 600000))
hconf.setMaxLifetime(c.getMillisecondsOr("maxLifetime", 1800000))
hconf.setLeakDetectionThreshold(c.getMillisecondsOr("leakDetectionThreshold", 0))
hconf.setInitializationFailFast(c.getBooleanOr("initializationFailFast", false))
c.getStringOpt("connectionTestQuery").foreach { s =>
  hconf.setJdbc4ConnectionTest(false)
  hconf.setConnectionTestQuery(s)
}
c.getStringOpt("connectionInitSql").foreach(hconf.setConnectionInitSql)
val numThreads = c.getIntOr("numThreads", 20)
hconf.setMaximumPoolSize(c.getIntOr("maxConnections", numThreads * 5))
hconf.setMinimumIdle(c.getIntOr("minConnections", numThreads))
hconf.setPoolName(name)
hconf.setRegisterMbeans(c.getBooleanOr("registerMbeans", false))

// Equivalent of ConnectionPreparer
hconf.setReadOnly(c.getBooleanOr("readOnly", false))
c.getStringOpt("isolation").map("TRANSACTION_" + _).foreach(hconf.setTransactionIsolation)
hconf.setCatalog(c.getStringOr("catalog", null))

初めに propertiesが呼ばれてそのまま設定され その後、デフォルトの設定が反映されるみたいです。

propertiesにmaximumPoolSizeを設定していても maxConnectionsが優先されるみたいです。 あと、HikariCP側と若干命名が変わっているです。

これが原因で、propertiesにmaximumPoolSizeとかを設定しても上書きされていました!!! なるほど。。。

# デフォルトの設定値
val numThreads = c.getIntOr("numThreads", 20)
hconf.setMaximumPoolSize(c.getIntOr("maxConnections", numThreads * 5))
hconf.setMinimumIdle(c.getIntOr("minConnections", numThreads))

設定の例

db.master {
    url  = "jdbc:mysql://hoge:3306/hoge"
    driver=com.mysql.jdbc.Driver
    user = hoge
    password = hoge
    connectionPool = HikariCP
    keepAliveConnection = true
    maxConnections = 10
    minConnections = 10
    connectionTimeout = 10000
    validationTimeout = 5000
    idleTimeout = 600000
    maxLifetime = 3000000
    leakDetectionThreshold = 0
    initializationFailFast = false
    readOnly = false
    catalog = null
    properties = {
        autoCommit = true
        initializationFailFast = true
        isolateInternalQueries = false
        allowPoolSuspension = false
    }
}

まとめ

propertiesに書いても、上書きされることを覚えておく!!