猫でもわかるWebプログラミングと副業

本業Webエンジニア。副業でもWebエンジニア。Web開発のヒントや、副業、日常生活のことを書きます。

Scala Play Framework と Slick で Connection Pool を利用する

f:id:yoshiki_utakata:20190406094749p:plain

経緯

私は、LGTM画像を簡単に作成できるサービス「LGTMoon」を公開しています。

lgtmoon.herokuapp.com

この LGTMoon は Heroku *1 で動いています。DBには Heroku PostgreSQL を使っています。 Heroku PostgreSQL は、接続できるコネクション数が20に制限されています。*2

f:id:yoshiki_utakata:20190406183655p:plain

アクセスが集中したり、重い処理が重なると、このコネクション制限に引っかかり、画像が表示されないなど、問題が起こることがありました。

コネクション数が20と制限されているのであれば、それに合わせてアプリ側でコネクションプールを張ったほうがいいんじゃないかと思いました。

今回は、 Scala, Play, Slick で、コネクションプールを使う方法をまとめます。

環境

  • Scala 2.11
  • Play Framework 2.5
  • Slick 3.2 (play-slick ではなくただのSlickです)

コネクションプールとは

通常アプリケーションからDBに接続する場合は

  1. APIにリクエストが来る
  2. コネクションを確立させて
  3. クエリを流して結果を取得
  4. コネクションを切る

という手順になります。

しかし、コネクションが20個埋まっている場合、「2. コネクション確立」で失敗になってしまい、エラーになってしまいます。

一方、コネクションプールを利用すると

  1. アプリ起動時にあらかじめコネクションを20個つくっておく
  2. APIリクエストが来る
  3. 空いてるコネクションを探す
  4. クエリを流して結果を取得

という手順になります。あらかじめアプリ起動時にコネクションを20個張ってしまうわけです。

各APIリクエスト時にm毎回コネクションを張る必要がなくなるので、コネクション確立のオーバーヘッドがなくなり、パフォーマンスが向上します。

コネクションプールを張ってなかったときの接続方法

conf/application.conf の設定

https://github.com/yoshikyoto/lgtmoon/blob/0a9a230d76b54f5f09fa7c5ee7103a4d995e0a58/conf/application.conf#L50-L56

# Database
pg_database = {
  url = "jdbc:postgresql://localhost/lgtmoon?user=postgres&password=postgres"
  driver = org.postgresql.Driver
  connectionPool = disable
  keepAliveConnection = true
}

DB接続部分

https://github.com/yoshikyoto/lgtmoon/blob/0a9a230d76b54f5f09fa7c5ee7103a4d995e0a58/app/infra/datasource/LgtmoonDatabase.scala#L12

Database.forConfig("pg_database")...

コネクションプールの利用

connectionPool の設定を enabled に変更するだけでは動きません。コネクションプールを利用するライブラリを設定し、コネクション数などのの設定を追加する必要があります。

configの書き換え

以下のように書き換えます

https://github.com/yoshikyoto/lgtmoon/commit/95bfaa605d2c24c035759d53282cb1aae630052b

pg_database = {
  url = "jdbc:postgresql://localhost/lgtmoon?user=postgres&password=postgres"
  driver = org.postgresql.Driver
  connectionPool = HikariCP
  properties = {
    # HikariCP の設定
    maximumPoolSize = 20
    minimumIdle = 10
  }
  keepAliveConnection = true
}

今回はHikari CPというコネクションプールのライブラリを利用します。Slickで利用できるコネクションプールライブラリはいくつかあるのですが、Hikari CPが一般的で最も広く利用されているようです。

maximumPoolSizeはその名の通りコネクションの最大数です。Heroku PostgreSQLのコネクションの最大値である20を設定します。

minimumIdle は 10 にしていますが、基本的には maximumPoolSize と同じ値にしたほうがいいとのことです。後ほど20に変更しようと思っています。

minimumIdleはアプリケーションにあまりリクエストが来ていないときのminimumのコネクション数ですが、常に上限いっぱいまで接続しておいたほうがパフォーマンスが良いためです。

Hikari CPライブラリの導入

build.sbtに以下を書きます。

https://github.com/yoshikyoto/lgtmoon/commit/95bfaa605d2c24c035759d53282cb1aae630052b

libraryDependencies ++= Seq(
  ...
  "com.typesafe.slick" %% "slick" % "3.2.3",
  ...
  "com.typesafe.slick" %% "slick-hikaricp" % "3.2.3"
)

もともと slick の 3.2.3 を使っていましたので、それに合わせて slick-hikaricp 3.2.3 を導入します。

これだけでコネクションプールの導入は完了です。

SQLアンチパターン

SQLアンチパターン

Amazon

*1:AWSのようなクラウドサービス

*2:Heroku PostgreSQLのhobby-basicプランです(9ドル/月)。無料のhobby-devプランでも同様20のコネクション制限があります。