ScalaのFutureがブロックする?(深く調べてない)
ScalaのFutureで(あるいはPlay?)で処理がブロックされてしまう現象があった。
環境
- Scala 2.10.1
- Play 2.1.3
- (Playのモデルクラスを使っているだけで、scalaコマンドから起動されるバッチ処理)
簡略化するとこんなコード(実際はもっと分割されてるし、whileじゃなくてtailrecだけど)。
import scala.concurrent.Await
import scala.concurrent.duration._
import play.api.libs.concurrent.Execution.Implicits._
for {
photo <- listOfPhotos
} yield {
// downloadはサードパーティライブラリのメソッドで
// Future[java.io.File]を返す
val fut = photo.download map { file =>
Logger.debug("start!") //ここまでは実行される。
// SomeApi.postFileの挙動は、responseが返るまでブロックする。
// しかし、↓が実行されない
SomeApi.postFile(file) map { jobId =>
var complete = false
var result = None
while (!complete) {
Thread.sleep(5000)
val job = SomeApi.getJob(jobId)
if (job.isComplete()) {
complete = true
result = job.getResult()
// resultに対して処理
}
}
}
}
try {
Await.result(fut, 120 seconds)
} catch {
case e: Exception => Logger.error(e.getMessage())
}
}
とりあえずの解決策
Futureに関する理解が甘かったので、勉強しつつ色々調べたんだけど、根本的な原因は良く分からなかった。でも、とりあえずの解決策が見つかった。以下のように、ExecutionContextを変えること。
//import play.api.libs.concurrent.Execution.Implicits._ //コメントアウトして import scala.concurrent.ExecutionContext.Implicits.global //こっちを追加
Playのバグなのか、自分のコードが悪いのか・・・そのうちPlay 2.2にアップグレードする予定なので、そしたら再度試してみよう。
(追記)Play! 2.2では直ってた
Play! 2.2にアップグレードしたところ、問題は解消した。
自分用メモ
Futureについて
直列で実行
複数のFutureの実行方法色々
- Asynchronous IO in Scala with futures
- Wait for several Futures
- Scala future sequence and timeout handling
- Scala Futures – built in timeout?
http://www.playframework-ja.org/documentation/2.1.5/ThreadPools
デフォルトのExecutionContextは、結局内部的にはAkkaのclassなので
https://github.com/playframework/playframework/blob/2.1.3/framework/src/play/src/main/scala/play/api/libs/concurrent/Execution.scala#L7-L8
https://github.com/playframework/playframework/blob/2.1.3/framework/src/play/src/main/scala/play/core/system/Invoker.scala
akkaの設定調節しないといけないんですかねぇ・・・
情報ありがとうございます。内部まで見てなかったです。
Akkaの設定変更は、今度試してみます。
違う観点ですが、こんなのもありました。
https://groups.google.com/forum/#!msg/play-framework/-XUoB1PFjjg/itlYaRBlC64J
Play 2.2にアップグレードしたら直ったっぽいです。