1円でも得したいWebエンジニアの日常

クーポンだったりクレジットカードのポイントだったりを利用して1円でも得しつつ生活を便利にしていきたいWebエンジニアによるブログ。技術的な記事から商品レビューなど日常的なことまで。

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

f:id:yoshiki_utakata:20190406094749p:plain

経緯

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

lgtmoon.herokuapp.com

このアプリケーションはHeroku上で動いています。バックエンドはHeroku PostgreSQLを使っています。このHeroku PostgreSQLなんですが、接続できるコネクション数が20に制限されています。*1

f:id:yoshiki_utakata:20190406183655p:plain

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

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

環境

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

コネクションプールとは

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

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

という手順を踏みます。ここで今回問題になるのが、コネクションが20個埋まっている場合は、1の「コネクション確立」で失敗になってしまい、エラーになってしまう点です。

しかしコネクションプールを利用すると

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

という手順になります。あらかじめアプリ起動時にコネクションを20個張ってしまうわけです。各APIリクエスト時にいちいちコネクションを張っておくので、コネクション確立のオーバーヘッドがなくなり、クエリが詰まることが減るだろうという算段です。

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

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")...

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

コネクションプールを利用するには、disabledを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が一般的で最も広く利用されているようで、それにならって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アンチパターン

失敗から学ぶRDBの正しい歩き方 (Software Design plus)

失敗から学ぶRDBの正しい歩き方 (Software Design plus)

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