Scala Play Framework 2.6 と Slick でコネクションプール(スレッドプール)の設定がしたい!
Scala の Play Framework と DB アクセスの Slick 、そして Play Framework のモジュールである play-slick を利用して Connection Pool を張った際にごにゃごにゃ設定したことのまとめです。
環境
- Scala 2.11
- Play Framework 2.6
- Slick 3.2.3
- play-slick 3.0.3
- PostgreSQL 10.9
前置き
コネクションプール/スレッドプールを使うモチベーション
僕の過去の記事があるのでそちらを参考にしてください。
コネクションプールとスレッドプールの違いは?
結局スレッドが20個プールしてあったらコネクションも20個あったほうが良いので、スレッドプールとコネクションプール、は同じものと考えて良いと思います。非同期の文脈の場合は「スレッドプール」と呼ぶっぽいです。
なぜ play-slick を使うようにしたのか
play-slickとは、Play Framework の application.conf をもとにいい感じに Slick を使えるようにするやつです。
↑の方で上げた記事で書いたように、もともとは play-slick を使わずに、Play と Slick を別々に入れて使っていました。コネクションプールも問題なく使えていました。しかし、Play 2.6 にバージョンアップした瞬間、maximumPoolSize が効かなくなりました。
maximumPoolSize が効かなくなり、おそらくデフォルトの設定が適用されるようになったのでしょう。コネクションプールが20以上のコネクションを求めるようになりアプリケーションが死ぬようになりました。
おそらくconfigのmaximumPoolSizeがうまくSlickに渡せていないのだと思いますが、試行錯誤した結果何が間違っているのかよくわかりませんでした。
どうやって config 渡せばいいんだーと調べている間に、 play-slick が config の書き換えだけで導入できることがわかりました。どうせ config 書き換えるなら play-slick も入れてしまおう。もしかしたら play-slick 入れたらちゃんとコネクションプールできるかも、と思いました。
結果 play-slick 入れたら簡単にコネクションプールの設定もできたので、play-slick を入れてからコネクションプールを導入するまでの流れを説明しようと思います。
play-slick の導入
基本的に Play のドキュメントや play-slick のドキュメントを読んでいけばできるかと思います。
- Play のデータベースのドキュメント
- play-slick のドキュメント
build.sbt に play-slick などを追加
build.sbt に依存パッケージを追加する必要があります。play-slick のドキュメントを見てみると。
- Play 2.6.x の場合
- Scala のバージョンは 2.11.x または 2.12.x
- play-slick のバージョンは 3.0.x
- Slick のバージョンは 3.2.x
と書いてあります。この条件を満たしかつこの記事を書いている時の最新バージョンは
- play-slick 3.0.3
- Slick 3.2.3
Scala のバージョンは 2.11.12 を使っていました。
ということでこうなりました。
// Scalaのバージョンは 2.11.12 を使う scalaVersion := "2.11.12" libraryDependencies ++= Seq( ..., // slick は 3.2.3 を使う "com.typesafe.slick" %% "slick" % "3.2.3", // 今回は PostgreSQL を利用する "org.postgresql" % "postgresql" % "42.2.6", // コネクションプールを使いたいので slick-hikaricp を入れておく // バージョンは slick のバージョンと合わせる "com.typesafe.slick" %% "slick-hikaricp" % "3.2.3", // play-slick を入れる。バージョンは 3.0.3 "com.typesafe.play" %% "play-slick" % "3.0.3" )
モデルからテーブルのマイグレーションを行う場合は play-slick-evolutions
を使うようなのですが、今回僕はこれは使いません。
application.conf
以下のような設定を追記しました。
# DB (Slick) slick.dbs = { default = { profile = "slick.jdbc.PostgresProfile$" db.driver = "org.postgresql.Driver" db.url = "jdbc:postgresql://127.0.0.1/lgtmoon?user=postgres&password=postgres" db.numThreads = 16 db.maxConnections = 16 } }
slick.dbs = {...}
と書いて、 {...}
の中に default = ...
を書くのは、 slick.dbs.default = ...
と書くのと同じ意味になります。
slick.dbs
以下には default
以外にもいろいろな設定をもたせられるのですが、 DB への接続時に何も指定しないと default
が使われることになるので、 default
で設定します。
profile や driver の設定は Play のドキュメントなどを読んでください。探せば出てくるはず。
db.numThreds と db.maxConnections
これらはコネクションプール向けの設定になります。これを書くと勝手にコネクションプールになるようです。書かなければコネクションプールにならないっぽい。
一番重要なのは db.numThreads
です。ここに設定したいコネクション数を指定します。 db.maxConnections
が本当は設定したいコネクション数なのですが、指定しなければ db.numThreads
と同じ値になります。 *1
ただし、過去には db.maxConnections = db.numThread * 5
がデフォルトになっていたこともあるようなので、ちゃんと db.numThreads
と同じ値を設定しています。
ちなみに、 numThread >= maxConnections になっていないと warning が出ます。基本は同じ値を設定しておけば問題ないです。
DB接続部分のコード
Slickの基本的なコードの書き方はここでは説明しません。Slickのドキュメントや他のブログなどを参考にしてください。
import javax.inject.Inject import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfigProvider} import slick.jdbc.JdbcProfile class ImageDatabase @Inject() ( protected val dbConfigProvider: DatabaseConfigProvider ) extends HasDatabaseConfigProvider[JdbcProfile] with ImageRepository { def getXxxx() = { ... db.run(action) ... } }
まず重要なのが、 HasDatabaseConfigProvider[JdbcProfile]
を extends します。これによって、プロパティ部分に db
が定義されるので、db.run(action)
が実行できるようになります。
問題はこの db
に設定を渡す方法で、これを DatabaseConfigProvider
経由で渡すようにします。 protected val dbConfigProvider: DatabaseConfigProvider
を定義すると、 HasDatabaseConfigProvider
はこの dbConfigProvider
を経由で接続設定をとってきてくれます。なのでこいつの名前は必ず dbConfigProvider
でなければいけないし、 protected val
でないといけません。
今回の場合特に接続の名前などは指定していないので、 default
が使われます。default以外を使う方法はドキュメントなどを参照してください。 dbConfigProvider
オブジェクトは @Inject
を書いておけば勝手にInjectされます。
まとめ
これで Scala Play で play-slick を使ってコネクションプールを貼ることができるかと思います。