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にアップグレードしたら直ったっぽいです。