Showing posts with label Passenger. Show all posts
Showing posts with label Passenger. Show all posts

Thursday, July 30, 2009

Passenger: Keeping ApplicationSpawner alive speeds up spawning an instance (updated)

前回 passenger プロセスの起動時間について書いたとき passenger のバージョンは 2.0.3 でしたが、あれから 9ヶ月、 2009-07-29 現在の最新バージョンは 2.2.4 になってます。

そのときは passenger の constants.rb を直接変更するという荒技で FrameworkSpawner と ApplicationSpawner のタイムアウト時間を長くしてましたが、いつのまにか RailsFrameworkSpawnerIdleTimeRailsAppSpawnerIdleTime という Apache の設定で変更できるようになってます。こいつらを 0 にすれば FrameworkSpawner は Apache を再起動するまで、 ApplicationSpawner は Apache を再起動するか、 touch restart.txt するまで持続します。

また、前回の記事 では Capistrano でデプロイしたときに (タイムアウトを延ばしているときは特に) ApplicationSpawner を KILL しないと古いデプロイの ApplicationSpawner がずっと残ると書いたんですけど、 2.2.0 の "Support for Capistrano-style deployments" により、 Capistrano 使ってても touch restart.txt で ApplicationSpawner が再起動するので KILL する必要は無くなりました。
(でも、2.2.0 から 2.2.2 までは 2.2.3 の "Fixed restarting of the ApplicationSpawner server" が修正されてなかったので worker process がみんなタイムアウトしている間に restart すると古い ApplicationSpawner が残っちゃってたみたいです。)

ちなみに、Apache 再起動直後の FrameworkSpawner も ApplicationSpawner もいないときと、 worker process だけ fork するときの速度をブラウザのページロード時間で測ると、(僕の環境では)5.6秒が1.0秒になるので、デフォルト設定で FrameworkSpawner がタイムアウトする時間30分に一度アクセスがあるかどうかわからないようなサービスではこの設定は必須かと思われます。

2009-7-30 10:00 追記


結論だけ言うと、 passenger-install-apache2-module を実行した直後に追加する apache の設定に1行追加すればOKです。

PassengerRoot /opt/ruby-enterprise-1.8.6-20090610/lib/ruby/gems/1.8/gems/passenger-2.2.4
PassengerRuby /opt/ruby-enterprise-1.8.6-20090610/bin/ruby
+ RailsAppSpawnerIdleTime 0

ApplicationSpawner が常駐することによるメモリ消費は僕の小さなアプリケーションREE を使っていれば private メモリで 25M 程度でした。これだけのコストでアプリケーションのレスポンスタイムの5秒ぐらいのロスが無くなるなら大歓迎でしょう。 Passenger のデフォルト設定でもいいかと思うぐらい。

Wednesday, October 22, 2008

Passenger: Keeping ApplicationSpawner alive speeds up spawning an instance

アクセス頻度が少ないサイトで Passenger を使っている場合、しばらくしてからアクセスするとデプロイ直後や apache のリスタート直後と同じくらいレスポンスが遅くなることがあります。

Passenger は fork 時の copy-on-write により複数のアプリケーションプロセスが消費する実メモリのサイズが小さくなるようになっていて、その fork も以下のような3段階で行っているようです。

  1. 最初に起動する spawn server から fork して、 Rails をロード (framework spawner)

  2. framework spawner から fork して、アプリケーションをロード (application spawner)

  3. application spawner から fork して、リクエストを処理する過程で必要なファイルをオートロード (application instance)


spawn server は apache が終了するまで生きているようなんですけど、 framework spawner と application spawner にはタイムアウトする時間が設定されていて、 Passenger 2.0.3 のデフォルトではそれぞれ、30分、10分となっています。
したがって、前回のアクセスから10分過ぎると、再度 application spawner から生成し直すのでレスポンスに時間がかかります。

ということは、 application spawner のタイムアウト時間を十分に長くすればよいということで、無理矢理 passenger のファイルを変更。

/usr/lib/ruby/gems/1.8/gems/passenger-2.0.3/lib/passenger/constants.rb:
-   FRAMEWORK_SPAWNER_MAX_IDLE_TIME = 30 * 60
- APP_SPAWNER_MAX_IDLE_TIME = 10 * 60
+ # keep them alive for a week
+ FRAMEWORK_SPAWNER_MAX_IDLE_TIME = 7 * 24 * 60 * 60
+ APP_SPAWNER_MAX_IDLE_TIME = 7 * 24 * 60 * 60


Passenger 2.0.3 の段階では上記のような変更が必要ですが、 将来のバージョンでは apache の設定ファイルで RailsFrameworkSpawnerIdleTime, RailsAppSpawnerIdleTime を指定すれば変更できるようになるようです。

また、 Phusion Passenger users guide - 8.3 Capistrano Recipe の note に書いてあるように、デプロイ後に新しい application spawner が生成されても古いのが残ったままになるので、デプロイするときに kill。

config/deploy.rb:
namespace :passenger do
namespace :application_spawner do
desc "kill Passenger ApplicationSpawner"
task :kill do
run "kill $( passenger-memory-stats | grep 'Passenger ApplicationSpawner' | awk '{ print $1 }' ) || true"
end
after "deploy:update_code", "passenger:application_spawner:kill"
end
end

Passenger のドキュメントに書かれているように "Passenger spawn server" を殺すには root 権限が必要なので代わりに application spawner を kill。なんとなく不安定な気もするけど、これで様子を見ることにします。

参考資料:
Wishlist — passenger — GitHub
RailsFrameworkSpawnerIdleTime and RailsAppSpawnerIdleTime - Phusion Passenger Discussions | Google Groups
Modrails ... Slow first request ... - Phusion Passenger Discussions | Google Groups