2008年04月08日(火)

Railsアプリを1日放置しているとMongrelが刺さる問題

カテゴリ: Mongrelがデッドロックする問題, Ruby, Ruby on Rails, トラブルシューティング このエントリーを含むはてなブックマーク はてなブックマーク - Railsアプリを1日放置しているとMongrelが刺さる問題

Mongrel + MySQLという構成のRailsアプリを1日程度まったくアクセスせずに放置すると、Mongrelが刺さって動かなくなるという非常に困った現象が発生する。原因もその対処もさっぱり分らない謎すぎる現象だが、実はその筋では割とよく知られた話であるようだ。

現象が発生するまでの時間がMySQLのwait_timeoutとKEEP_ALIVEなTCPソケットのTIME_WAIT期間が終わるまでの10時間もかかるため、なかなか再現しないイヤな問題である。MongrelのFAQにwait_timeoutととの関連は示されているが、それを短くしてもまだTIME_WAITの2時間があるため、再現しないと悩むことになる。

Rails + MySQLという構成のウェブアプリは多数あるはずなのに大問題になっていないのは、ActiveRecordにバンドルされているピュアRubyのMySQLドライバとMongrelを一緒に使用した場合にだけ遭遇する問題だからのようだ。前述の記事にあるようにマルチスレッド状態でない場合は発生しないため、FastCGIならば問題ないはずである。Mongrelはマシンに搭載されているプロセッサの数だけワーカースレッドを作るので、必ずマルチスレッドになる。

つまりこの問題は「ピュアRubyのMySQLドライバ」「Mongrel」「10時間の無アクセス期間」の3つが重なったときだけ発生すると考えられる。したがって以下のようにいずれかを避けることで問題を回避することができるはずである。

  1. MySQLのクライアントライブラリを使用したバイナリ版のドライバをインストールする
  2. Mongrelの使用をやめ、FastCGIなどにする
  3. MySQLのコネクションがタイムアウトしないようcron等で定期的にアクセスする

それにしてもいったいどこで刺さるのだろうか。マルチスレッドかそうでないかで挙動が変わるのは、スレッドがひとつしかない状態では rb_thread_fd_writable() が rb_thread_schedule() の呼び出しを省略してスレッドのスケジューリングを行わないからのようだ。rb_thread_schedule() で実行がブロックされそうなところは select() くらいしかない。select() から帰ってこなくなるのか、あるいはシグナルが無視されるなどして、該当スレッドにディスパッチする機会がなくなってしまうのか。これ以上はソースコードを読むだけでは難しい。

コメントを投稿する





このエントリへのトラックバックURL