注意
挙動を試してないので確証はまだないけど、とりあえずの備忘として残した記事です。
突然の死
ある日突然リクエストを受け付けるが処理が走らずエラーを返すようになってしまった。
正直なぜそうなったのか分からず、なんやかんやあってスレッドが怪しいのでは?となった。
スレッドの動き
netty のスレッド -> playframework のスレッド -> slick のスレッド
らしい。
DBIO の処理
DBIOの処理はこんな感じで書かれていた
for {
_ <- DBIO型を返す
_ <- DBIO型を返す
} yield ()
for は flatMap になる
slick の flatMap による DBIO合成
def flatMap[R2, S2 <: NoStream, E2 <: Effect](f: R => DBIOAction[R2, S2, E2])(implicit executor: ExecutionContext): DBIOAction[R2, S2, E with E2] =
FlatMapAction[R2, S2, R, E with E2](this, f, executor)
flatMapによる合成は implicit executor: ExecutionContext を要求する
playframework のスレッドで動いていた処理が slick でのDBIOの処理に移る(slickのスレッドに移る)とコンテキストスイッチが発生する。(らしい)
たしかに ExecutionContext を分けているのでそうなのだろう。
flatMapされる毎にコンテキストスイッチが発生。
play thread -> slick thread
play thread <- slick thread
というような状況になり、ある時
play thread 1 -> slick thread 1
slick thread 1に移った時点で play thread は開放されているので別の処理で使われる
play <- slick thread 1
slick thread 1 の処理が終わった時点で play thread が空いていないと処理を返せないので slick thread 1を握ったまま待つ。
play thread 1 -> slick
slick thread が空いていないので slick thread が空くのを待つ。
という状況に陥ったのでは無いかという推測。
ちなみに
DBIO.seq とか andThen であれば ExecutionContext を要求しないので同じスレッドで動くのでは?というお話
しかも
最悪なことに Await.result(しかも Duration.Inf ) されていたため、処理を待ち続けてスレッドを掴み続けていました。
ていうか
問題発生時にRejectedExecutionException出さないの?
やっぱりよくわからん。
Await.result の Duration.Infで延々と待ち続けるからエラー吐かないとか?