乗り換えた経緯と説明
とりあえずDBからデータ取ってくる処理を全部 Await.result でやってたのですが、様々な理由でFutureに乗り換えることにしました。
Await.resultがダメな理由としてはこちらの方の記事が参考になります。
ざっくり説明すると
- Await.result : ずっと処理を待ち続けてスレッドが解放されない
- Future : 処理が終わったら処理を再開する。それまでスレッドは解放される
スクリプトの実行が終わるまでずっとPCの前で待ち続けている状態だと思うとイメージしやすいかなと。
Await.resultのイメージ
スクリプトが終わるまでずっと画面見て待ってる。
他の作業ができない
使用スレッド数は 2
- スクリプト実行のプロセス
- スクリプト返ってくるまでずっと画面見てる人物

Futureのイメージ
スクリプト実行が終わってから処理をすればいいとわかってる。
他の事ができる。ラーメンも食べれる
使用スレッド数は 1
- スクリプト実行のプロセスのみ

てなことで、ラーメンが食べれるFutureに乗り換えていきましょう。
DBの取得処理をAwait.resultからFutureに変える
今のコードがこんな感じです。
def findUser(userId: Int): Try[Option[User]] = {
val sql =
sql"""
SELECT id, name
FROM user
WHERE user_id = "#${userId}"
""".as[User]
Try(Await.result(db.run(sql), Duration.Inf))
}
これをFutureにこんな感じで変えてみます。 db.run
に変えただけですね。
def findUser(userId:Int): Future[Option[User]] = {
db.run(
sql"""
SELECT id, name
FROM user
WHERE user_id = "#${userId}"
""".as[User]
)
}
これでFuture型で返ってくるようになります。他のやり方もあるかと思いますので一例程度に。
コードの使用側をFutureに変える
余談ですが、 Await.result
だと処理が終了してから返ってきますので get
してもOKです。
反対に、 Future
は処理が終わってない状態のものが返ってくるので直接 get
とかしてはダメです。
None.get
になります。(これで1回実装してやらかした)
今のコードがこんな感じです。
- Tryの中身をmatchで取得して、Optionの中身をgetで取得
// 返り値 : Try[Option[User]]
hogeRepository.findUser match {
case Success(OptionUser) =>
OptionUser.get
case Failure(e) => // エラー処理
}
こんな感じで書き換えました。
- mapでFutureが返ってから以降の処理を実行
- Optionの中身をgetで取得
// 返り値 : Future[Option[User]]
hogeRepository.findUser.map { maybeUser =>
maybeUser.get
}
Futureのエラー処理は recover
で書けるみたいなので、最終的にはこんな感じです。
// 返り値 : Future[Option[User]]
val futureUser = hogeRepository.findUser.map { maybeUser =>
maybeUser.get
}.recover {
// エラー処理
case e => {
println(e)
System.exit(1)
}
}
呼び出し側も変えなきゃダメなので途中から移行を考えてる人はちょっと骨が折れるかもしれませんね。
これで大量のアクセスにも耐えれそうなFutureの恩恵を受けれるコードに書き換える事ができました!
余談
処理の流れの中で、 Seq[Future[String]]
みたいになって Future[Seq[String]]
にしたい!なんて時は sequence
を使うといい感じに処理できます。
val seqFutureStrings :Seq[Future[String]] = hogeRepository.findStrings
// Futureの入れ子の順番を逆にする
val futureSeqStrings :Future[Seq[String]] = Future.sequence(futureNames)
futureSeqStrings.map { Strings =>
Strings.mkString(",")
}