Atom上でRailsプロジェクト内のファイル移動を楽にするRails-Transporterというパッケージを書いた

最近Emacsからgithub製のテキストエディターであるAtomに移行しました。慣れるまで1週間ほど移行期間は必要でしたがデフォルトの設定で十分高機能で使いやすいです。自分は普段の仕事ではRailsアプリの開発をする事が多いのでRails開発で使える便利そうなパッケージを探してみたんですが、自分にあいそうなものがなかったのでrails-transporterというパッケージを作りました。

f:id:hakutoitoi:20140521011332g:plain

プロジェクト内のファイル移動を簡単にすることを目的にしており、下記のコマンドが使えるようになります。

20150323 追記

Command

open-controller(ctrl-r c)

model, view, controller-specのどれかを開いてる時、

app/models/blog.rb
app/views/blogs/show.html.erb
spec/controllers/blogs_controller_spec.rb

関連するcontrollerを開きます。

app/controllers/blogs_controller.rb

open-view(ctrl-r v)

controllerのアクションメソッド内カーソルがある時に対応するviewを開きます。

下記例でindexメソッド内にカーソルがあれば対応するviewを開きます。

class UsersController < ApplicationController
  def index
    @users = User.all
  end
 
  def show
    @user = User.find(params[:id])
  end
end

open-view-finder(ctrl-r v f)

controller, modelのどれかを開いてる時、

app/controllers/blog_controller.rb
app/models/blog.rb

関連するviewのリストを開きます

app/views/blogs/show.html.erb
app/views/blogs/index.html.erb

open-model(ctrl-r m)

controller, view, model-specのどれかを開いてる時、

app/controllers/blogs_controller.rb
app/views/blogs/show.html.erb
spec/models/blog_spec.rb

関連するmodelを開きます。

app/models/blog.rb

open-helper(ctrl-r h)

controllerを開いてる時、

app/controllers/application_controller.rb

関連するhelperを開きます。

app/helpers/application_helper.rb

open-spec(ctrl-r s)

controller, helper, modelのどれかを開いてる時、

app/controllers/blogs_controller.rb
app/helpers/blogs_helper.rb
app/models/blog.rb

関連するspecを開きます。

spec/controllers/blogs_controller_spec.rb
spec/helpers/blogs_helper_spec.rb
spec/models/blog_spec.rb

open-partial-template(ctrl-r p)

view内のrenderメソッドにカーソルがある時、

render "form"

関連する部分テンプレートを開きます。

app/views/blogs/_form.html.erb

open-asset(ctrl-r a)

カーソルがstylesheet_link_tag、javascript_include_tagにある時、

  <%= javascript_include_tag "application" %>
  <%= stylesheet_link_tag    "application" %>

関連するassetマニフェストファイルを開きます。

app/assets/javascripts/application.js
app/assets/stylesheets/application.js

assetマニフェストファイル内のrequireメソッドにカーソルがある時、

//= require blogs

関連するassetファイルを開きます。

app/assets/javascripts/blogs.js.coffee

assetマニフェストファイル内のrequire_tree、require_directoryにカーソルがある時、

//= require_tree shared
//= require_directory shared

関連するassetファイルのリストを開きます。

app/assets/javascripts/shared/file1.js.coffee
app/assets/javascripts/shared/file2.js.coffee
app/assets/javascripts/shared/file3.js.coffee

open-migration(ctrl-r d m)

migrationのリストを開きます

db/migrate/20140502021836_create_blogs.rb
db/migrate/20140512015127_create_entries.rb

open-layout(ctrl-r l)

controllerに対応するlayoutファイルを開きます。

open-factory(ctrl-r f)

model、model-specに対応するfactoryファイルを開きます。

RailsでArgumentError: invalid byte sequence in UTF-8が発生する場合の解決策

Railsで作成されたアプリケーションのリクエストURLにURLエンコード(壊れていてUTF8にデコードできない)された文字列を含めるとArgumentErrorが発生します。

環境

ruby 2.0.0p353
rails-4.0.4

再現方法

サンプルプロジェクトを構築

# railsの最新版をインストール
$ gem install rails

# プロジェクトのセットアップ
$ rails new invalid-byte-sequence-in-utf8-demo
$ cd invalid-byte-sequence-in-utf8-demo
$ bundle install

# 開発サーバ起動
$ rails server
=> Booting WEBrick
=> Rails 4.0.4 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
[2014-03-30 16:02:55] INFO  WEBrick 1.3.1
[2014-03-30 16:02:55] INFO  ruby 2.0.0 (2013-11-22) [x86_64-darwin13.0.0]
[2014-03-30 16:02:55] INFO  WEBrick::HTTPServer#start: pid=2000 port=3000

サンプルURL

# 正常なリクエストは成功
http://localhost:3000

# 不正な文字列を付与したリクエストは失敗
http://localhost:3000/?%8E%96%9B%26frac12%3Bae%9A%84%97%94%9F%9A%84%26sup3%3B%84ae%80
ArgumentError - invalid byte sequence in UTF-8:が発生する。

対応方法

この例外を無視する事もできるが解決する方法も2つある。

例外が発生するより前の段階で400 bad requestを返すようにする

このブログポストに書いてあり通り、Rackミドルウェアを追加してURLがUTF8にデコードできるかどうか検査して、できない場合は400エラーを返すシンプルな実装方法。

デコードできない文字列を無視して処理する

utf8-cleanerを使ってデコードできない文字列を無視してしまって、正常にデコード出来る文字列だけを処理する。

utf8-cleanerがやってる事

lib/utf8-cleaner/middleware.rb
1. Rackミドルウェアを追加してsanitize_envメソッドが各リクエストの前処理として呼び出される
2. sanitize_envメソッドではHTTP_REFERERやPATH_INFOといったURLに関連する環境変数のチェックをしている
3. 各環境変数に%が含まれる場合は後述のuri_string.rbで定義されているURIString#cleanedメソッドにその値を渡して実行している

module UTF8Cleaner
  class Middleware

    SANITIZE_ENV_KEYS = [
     "HTTP_REFERER",
     "PATH_INFO",
     "QUERY_STRING",
     "REQUEST_PATH",
     "REQUEST_URI",
     "HTTP_COOKIE"
    ]

    def initialize(app)
     @app = app
    end

    def call(env)
     @app.call(sanitize_env(env))
    end

    private

    def sanitize_env(env)
      SANITIZE_ENV_KEYS.each do |key|
        next unless value = env[key]

        if value.include?('%')
          env[key] = URIString.new(value).cleaned
        end
      end
      env
    end
  end
end

utf8-cleaner/lib/utf8-cleaner/uri_string.rb
1. cleanedメソッドはまず渡された文字列がUTF8としでデコードできるか検査する
2. 文字列に問題なければなにもしない、問題がある場合は文字列を1文字ずつ検査
4. 文字が'%'の場合はその次とその次の文字をチェックして何バイトのUTF8の文字になるかをチェック
5. 不正な文字として判断されたらURL文字列から除かれる
6. 最終的に不正な文字列は除かれているのでArgumentErrorは発生しなくなる

module UTF8Cleaner
  class URIString
    attr_accessor :data

    def initialize(data)
      self.data = data
    end

    def cleaned
      if valid?
        data
      else
        encoded_char_array.join
      end
    end

    def encoded?
      data.include?('%')
    end

    def valid?
      valid_uri_encoded_utf8(data)
    end

    private

    # Returns an array of valid URI-encoded UTF-8 characters.
    def encoded_char_array
      char_array = []
      index = 0

      while (index < data.length) do
        char = data[index]

        if char == '%'
          # Skip the next two characters, which are the encoded byte
          # indicates by this %. (We'll change this later for multibyte characters.)
          skip_next = 2

          # How long is this character?
          first_byte = '0x' + (data[index + 1] + data[index + 2]).upcase
          bytes = utf8_char_length_in_bytes(first_byte)

          # Grab the specified number of encoded bytes
          utf8_char_encoded_bytes = next_n_bytes_from(index, bytes)

          # Did we get the right number of bytes?
          if utf8_char_encoded_bytes.length == bytes

            # We did. Is it a valid character?
            utf8_char_encoded = utf8_char_encoded_bytes.join

            if valid_uri_encoded_utf8(utf8_char_encoded)
              # It's valid!
              char_array << utf8_char_encoded

              # If we're dealing with a multibyte character, skip more than two
              # of the next characters, which have already been processed.
              skip_next = bytes * 3 - 1
            end
          end
          index += skip_next
        else
          # This was not an encoded character, so just add it and move to the next.
          char_array << char
        end
        index += 1
      end

      char_array
    end

    def valid_uri_encoded_utf8(string)
      URI.decode(string).force_encoding('UTF-8').valid_encoding?
    end

    # Grab the next num_bytes URI-encoded bytes from the raw character array.
    # Returns an array like ['%E2', '%9C', '%93']
    def next_n_bytes_from(index, num_bytes)
      return [] if data.length < index + (3 * num_bytes)

      num_bytes.times.map do |n|
        # Look for percent signs in the right places
        pct_index = index + (3 * n)
        if data[pct_index] == '%'
          byte = data[pct_index + 1..pct_index + 2]
        else
          # An expected percent sign was missing. The whole character is invalid.
          return []
        end
        '%' + byte
      end
    end

    # If the first byte is between 0xC0 and 0xDF, the UTF-8 character has two bytes;
    # if it is between 0xE0 and 0xEF, the UTF-8 character has 3 bytes;
    # and if it is 0xF0 and 0xFF, the UTF-8 character has 4 bytes.
    # first_byte is a string like "0x13"
    def utf8_char_length_in_bytes(first_byte)
      if first_byte.hex < 'C0'.hex
        1
      elsif first_byte.hex < 'DF'.hex
        2
      elsif first_byte.hex < 'EF'.hex
        3
      else
        4
      end
    end

  end
end

まとめ

utf8-cleanerを使うことでこのようなリクエストであっても正常に処理できるがそもそもリクエストされたURLを改変して処理するのでもはや処理する意味がない気がする。参考にさせてもらったブログの人がthis issue is not super-importantって言ってるのに同意でこの例外は無視してもいいし、気になる人は400エラーを返すのが一番綺麗なやり方かなと思う。
それでもこのようなリクエストを正常なレスポンスで返したい場合は自己責任でutf8-cleanerを使うといいと思う。

Apache Passengerの設定項目について調べた

PassengerRailsを動かす事ができるアプリケーションサーバである。
Apache版の設定項目について調べたので自分用の備忘録として残しておく。

バージョン

Passenger-4.0.37
http://www.modrails.com/documentation/Users%20guide%20Apache.html

Configuring Phusion Passenger

PassengerRoot

http://www.modrails.com/documentation/Users%20guide%20Apache.html#_passengerroot_lt_directory_gt
apacheのpassengerモジュールの置いてあるディレクトリを指定する。
普通はインストーラを実行した時に表示されるのでそれを使えばよい。rubyのインストール場所をかえた場合などはこちらの値も変更する必要がある。

PassengerDefaultRuby

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerDefaultRuby
アプリケーションとInternalHelper(例:PassengerPreStart)が使うRubyインタプリタを指定する。
PassengerRoot同様にインストーラ実行時に表示されるのでそれを使えば良い。

Deployment options

PassengerEnabled

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerEnabled
指定したコンテクストでPassengerを無効化出来る。

PassengerRuby

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerRuby
VirtualHostやDirectory等のコンテクストで使うRubyインタプリタを指定できる。PassengerDefaultRubyをOverrideできる。
この設定ができることにより、アプリケーション毎に使うRubyのバージョンを混在することが可能になる。
ただし、InternalHelperスクリプトは常にPassengerDefaultRubyで指定されたインタプリタを使う。

PassengerPython

PassengerRubyと同じ。

PassengerNodejs

PassengerRubyと同じ。

PassengerAppEnv

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerAppEnv
下記の環境変数を指定する。stagingなど指定することでアプリケーションの環境を切り替える事ができる。
RAILS_ENV
RACK_ENV
WSGI_ENV
NODE_ENV
PASSENGER_ENV

RailsEnv

PassengerAppEnvのエイリアス

RackEnv

PassengerAppEnvのエイリアス

PassengerAppRoot

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerAppRoot
アプリケーションのルートディレクトリを指定する。
デフォルトはDocumentRootの親ディレクトリ

PassengerRestartDir

http://www.modrails.com/documentation/Users%20guide%20Apache.html#_passengerrestartdir_lt_directory_gt
アプリケーションを再起動するためにはrestart.txtをtouchする必要がある。
restart.txtを配置するディレクトリを指定する。
デフォルトはPassengerAppRoot配下のtmpディレクトリ。

PassengerRollingRestarts(enterprise限定)

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerRollingRestarts

OSS版の最大の弱点と言っても過言ではないのがdeploy後のダウンタイムである。Rolling Restartをonにすることでunicornのようにゼロダウンタイムdeployが可能になる。

通常のRestartの場合、workerプロセスを一度全てshutdownしてから新しいプロセスを作るのに対して、1プロセスずつ入れ替えてdeployしてくれるのがRollingRestartsである。

Phusion Passenger Enterprise: rolling restarts from Phusion on Vimeo.

PassengerResistDeploymentErrors(enterprise限定)

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerResistDeploymentErrors
DeployしたコードにSyntaxErrorなどがあった場合、壊れたコードは実際には使われずにDeploy前のバージョンが使われてDeployに失敗した事がログに出力される。
この機能はRollingRestart機能が有効になっていないと作動しないので注意が必要だ。

Phusion Passenger Enterprise: deployment error resistance from Phusion on Vimeo.

Process spawning options

PassengerSpawnMethod

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerSpawnMethod
worker生成の方法を指定する。
smartとdirectがありsmartはアプリケーションコードをcacheするために生成がdirectに比べて速いが、アプリがMemcachedとのコネクションを持っている場合などは注意が必要になる。
RailsMemcachedを使っている場合は下記設定をアプリのコードに書いてworkerプロセス毎に再接続を行う必要がある。

if defined?(PhusionPassenger)
    PhusionPassenger.on_event(:starting_worker_process) do |forked|
        if forked
            # We're in smart spawning mode.
            reestablish_connection_to_memcached
        else
            # We're in direct spawning mode. We don't need to do anything.
        end
    end
end
PassengerLoadShellEnvvars

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerLoadShellEnvvars
シェルがbashの場合、環境変数をロードする。

Security options

PassengerUserSwitching

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerUserSwitching
全てのPassengerプロセスを同一のユーザーにしないことでセキュリティを担保する

PassengerGroup

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerGroup
Passengerのプロセスはデフォルトではconfign/enviroment.rbかconfig.ruファイルの所有グループを引き継ぐが、
この設定を入れることで上書きすることができる。

PassengerDefaultUser

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerDefaultUser
user switching supportが失敗した またら無効の場合に、このオプションで指定したユーザーでプロセスが動く。
デフォルトはnobody。

PassengerDefaultGroup

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerDefaultGroup
user switching supportが失敗した またら無効の場合に、このオプションで指定したグループでプロセスが動く。
デフォルトはPassengerDefaultUserのグループ。

PassengerFriendlyErrorPages

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerFriendlyErrorPages
Passengerを立ち上げる時にDBの設定が間違っていたりすると立ち上げに失敗してエラーの原因をbacktraceとともに下記のような画面を表示してくれて嬉しいのだが、
f:id:hakutoitoi:20140308174011p:plain
プロダクション環境でこのメッセージを表示してしまうとサーバの情報を外部に漏らす可能性がある。この設定をoffにすることで非表示にできる。

Resource control and optimization options

下記動画にあるように、Enterprise版だとより柔軟なリソースコントロールが可能になる。

Phusion Passenger Enterprise: resource control from Phusion on Vimeo.

PassengerMaxPoolSize

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerMaxPoolSize
1つのサーバで立ち上がるWorkerプロセスの最大値を設定する。
マルチスレッドモデルを使わない場合はこの数値がPassengerが同時に処理することができるリクエスト数になる。
サーバのメモリが足りない場合は数値を下げる必要がある。
デフォルトは6。
3に指定した場合、立ち上がるworkerプロセスはサーバ内で最大でも3個までとなる。

$ ps aux | grep "[P]assenger RackApp"
u     3778  5.0  6.3 141180 109172 ?       Sl   07:20  11:25 Passenger RackApp: /var/www/test-app/current
u    27640  3.6  6.3 141560 109604 ?       Sl   03:21  16:54 Passenger RackApp: /var/www/test-app/current
u    29392  3.9  6.4 142200 111356 ?       Sl   04:07  16:38 Passenger RackApp: /var/www/test-app/current

QueueSizeという名前が混乱を招くがworkerプロセスの数を意味する。
設定の目安はPhusionのブログに書いてあり、
http://blog.phusion.nl/2013/03/12/tuning-phusion-passengers-concurrency-settings/
シングルスレッドの場合の計算式:

max_app_processes = (TOTAL_RAM * 0.75) / RAM_PER_PROCESS

サーバのメモリが1.7GBで1プロセスのメモリ使用が100MBの場合は:
12.75 = (1700MB * 0.75) / 100MB

マルチスレッドの場合の計算式:

max_app_threads_per_process = ((TOTAL_RAM * 0.75) - (NUMBER_OF_PROCESSES * RAM_PER_PROCESS * 0.9)) / (RAM_PER_PROCESS / 10)
NUMBER_OF_PROCESSESはRuby(MRI)の場合CPUの数で考えていいようだ(JRUBYやRubiniusの場合は1で考える)。

Rubyを使っていてサーバのCPUが2でメモリが1.7GBで1プロセスのメモリ使用が100MBの場合は:
109.5 = ((1700MB * 0.75) - (2 * 100MB * 0.9)) / (100MB / 10)

※32bitシステムの場合はmax_app_threads_per_processは200より大きくするべきではない

上記の計算結果を設定してあげればよい。

#シングルスレッドの場合:
PassengerMaxPoolSize 12

# マルチスレッドの場合
PassengerMaxPoolSize 2(CPUの数)
PassengerThreadCount 109
PassengerMinInstances

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerMinInstances
全体または1つのアプリケーションで立ち上げるWorker数の下限をglobalとアプリケーションどちらにでも指定する事が可能。

PassengerMaxInstances(enterprise限定)

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerMaxInstances
全体または1つのアプリケーションで立ち上げるWorker数の上限をglobalとアプリケーションどちらにでも指定する事が可能。
例えば、websiteとblogをvirtulhostに設定していてwebsiteへのアクセスが多い場合は下記のように設定することでwebsiteの同時実行処理リクエスト数を2にするといったカスタマイズが可能である。

PassengerMaxRequestQueueSize 3
<VirtualHost *:80>
    PassengerMaxInstances 2
    RailsEnv production
    ServerName www.hakutoitoi.com
</VirtualHost>
<VirtualHost *:80>
    PassengerMaxInstances 1
    RailsEnv production
    ServerName blog.hakutoitoi.com
</VirtualHost>

プロセスリスト:

$ ps aux | grep "[P]assenger RackApp"
u     3778  5.0  6.3 141180 109172 ?       Sl   07:20  11:25 Passenger RackApp: /var/www/website/current
u    27640  3.6  6.3 141560 109604 ?       Sl   03:21  16:54 Passenger RackApp: /var/www/website/current
u    29392  3.9  6.4 142200 111356 ?       Sl   04:07  16:38 Passenger RackApp: /var/www/blog/current
PassengerMaxInstancesPerApp

http://www.modrails.com/documentation/Users%20guide%20Apache.html#_passengermaxinstancesperapp_lt_integer_gt
一つのアプリケーションで立ち上げるWorkerプロセスの上限をグローバルで指定する事が可能。
PassengerMaxInstancesのようにアプリケーションごとに柔軟にプロセス数を指定することはできないので全てのアプリケーションの最大プロセス数は同一になる。

PassengerMaxRequestQueueSize 2
PassengerMaxInstancesPerApp 1
<VirtualHost *:80>
    RailsEnv production
    ServerName www.hakutoitoi.com
</VirtualHost>
<VirtualHost *:80>
    RailsEnv production
    ServerName blog.hakutoitoi.com
</VirtualHost>

プロセスリスト:

$ ps aux | grep "[P]assenger RackApp"
u     3778  5.0  6.3 141180 109172 ?       Sl   07:20  11:25 Passenger RackApp: /var/www/website/current
u    27640  3.6  6.3 141560 109604 ?       Sl   03:21  16:54 Passenger RackApp: /var/www/blog/current
PassengerPoolIdleTime

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerPoolIdleTime
1つのwokerが設定した秒数の間idle状態が続いた場合、そのworkerはメモリ節約のためshutdownされる。
デフォルトは300秒。
idle状態がどのくらい続いてるかはpassenger-statusコマンドのLast usedの項目で確認できる。

$ sudo /usr/local/bin/passenger-status  | grep -B 1 "Last used"
  * PID: 27640   Sessions: 0       Processed: 14570   Uptime: 9h 10m 6s
    CPU: 3%      Memory  : 103M    Last used: 23s ago
  * PID: 29392   Sessions: 0       Processed: 14477   Uptime: 8h 24m 0s
    CPU: 4%      Memory  : 104M    Last used: 4m 40s ago

この例だとPID29392のworkerがidle状態のまま4分40秒経っているためもうすぐshutdownされる。

PassengerMaxPreloaderIdleTime

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerMaxPreloaderIdleTime
workerを生成・破棄するSpawnerプロセスがshutdownするまでのidle時間の設定。
デフォルトは300秒で、その間workerプロセスに変動がなければshutdownされる。

PassengerStartTimeout

http://www.modrails.com/documentation/Users%20guide%20Apache.html#_passengerstarttimeout_lt_seconds_gt
workerプロセスの開始に設定した秒数以上かかったら失敗する。
デフォルト90秒。

PassengerConcurrencyModel(enterprise限定)

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerConcurrencyModel
マルチスレッドモデルを有効にできる。

PassengerThreadCount(enterprise限定)

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerThreadCount
マルチスレッドが有効な時に一つのプロセスがいくつのスレッドを使えるようにするか設定する。
デフォルトは1。

PassengerMaxRequests

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerMaxRequests
ここに設定したリクエスト数を処理するとwokerプロセスが再起動する。
アプリケーションがメモリリークを起こしている場合には必要な設定だろう。
デフォルトは0で無効になっている。

PassengerMaxRequestTime(enterprise限定)

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerMaxRequestTime
リクエストの処理時間が設定値を超えたらworkerを再起動する。
デフォルトは0で無効になっている。
リクエストの処理に時間がかかりすぎてアプリケーション全体がつまって止まってしまう危険性を減らす事ができる。
下記の設定をすると/expensive_computationへのリクエストだけtimeoutまでの時間を伸ばせるといった柔軟な設定を行える。

<VirtualHost *:80>
    ServerName www.example.com
    DocumentRoot /webapps/my_app/public

    PassengerMaxRequestTime 2
    <Location /expensive_computation>
        PassengerMaxRequestTime 10
    </Location>
</VirtualHost>

この設定が必要になる時は大体アプリケーションが遅いことに問題があるので、問題を特定して迅速に修正すべきだろう。

PassengerMemoryLimit(enterprise限定)

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerMemoryLimit
ここに指定したメモリ数を超えるメモリをwokerプロセスが使うと再起動する。
アプリケーションがメモリリークを起こしている場合には必要な設定だろう。
デフォルトは0で無効になっている。

PassengerStatThrottleRate

http://www.modrails.com/documentation/Users%20guide%20Apache.html#_passengerstatthrottlerate_lt_integer_gt
設定ファイル(config/envrironment.rb)や再起動ファイル(restart.txt)をチェックするタイミングを指定できる。
0の場合は各リクエストを処理するタイミングで行う。
秒数を指定した場合はその秒数毎にチェックする。

PassengerPreStart

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerPreStart
デフォルトではPassengerは最初にリクエストがあるまでプロセスをstartさせない。この設定を入れることでapacheを起動した時に指定したURLだけはアクセスがなくてもアプリケーションを事前にstartしてくれる。

PassengerHighPerformance

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerHighPerformance
onに設定すると少し高速化されるがmod_rewriteやmod_autoindexなどのモジュールが使えなくなる。

Connection handling options

PassengerBufferUpload

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerBufferUpload
onの場合、POSTでアップロードされたデータはアプリケーションに送られる前にバッファされる。遅いクライアントからのアップロードを守るが、アップロード進捗を追跡する機能は使えなくなる。
デフォルトはon。

PassengerBufferResponse

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerBufferResponse
onの場合、apacheにレスポンスをバッファさせる。

apache版のPassengerの場合、2つのレスポンスバッファリングシステムが有効である。

  • Apache response buffering system(PassengerBufferResponseでon/offできる)
  • Phusion Passenger response buffering system(この設定はPassengerBufferResponseがoffでも常に有効である)

基本的にはPassengerのほうがバッファしてくれるので遅いクライアントからの攻撃があってもoffで問題はない。
もし何がしかの理由でapacheレベルのバッファリングをしたい場合は、このオプションをonにすることで有効にすることができる。

注意して欲しいのはこのオプションをonにした場合はストリーミングレスポンスを作るのが不可能になること。
下記のコードは1秒毎にレスポンスを返すコードだがPassengerBuggerResponseをonにした場合は10秒後に全体のレスポンスを返す挙動になってしまう、offの場合なら期待通りに動く。

render :text => lambda { |response, output|
    10.times do |i|
        output.write("entry #{i}\n")
        output.flush
        sleep 1
    end
}

※大きいサイズのレスポンスを返す場合はこの設定はoffにすべきだろう、なぜならレスポンスはメモリにバッファされるのでメモリ使用量が極端に大きくなってしまう。

PassengerErrorOverride

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerErrorOverride
400番以上のエラーページをPassengerデフォルトのものから自分の好きなページに変更できる。

PassengerMaxRequestQueueSize

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerMaxRequestQueueSize
全てのwokerがリクエスト処理中の状態で新規のリクエストがくるとリクエストキューに格納されてidleになったwokerが順次このキューにたまったリクエストを処理するのだがこのキューの最大サイズをアプリケーション毎に設定できる。
デフォルトは100で、100を超えたらキューにはためずにクライアントに503エラーを返すようになる。
この動作は非常に重要でアプリケーションになんかしらの問題が起きている事を把握できる。例えば:

  • PVがいきなり増加した
  • フルテーブルスキャンやフルインデックススキャン等の非常に時間のかかる処理を行うようになった
  • 巨大なテーブルにインデックスの追加を行ったため、テーブルにWRITEロックがかかり全てのwokerが更新処理中でロック解除待ちになっている

などなど。

キューにたまっているリクエスト数はpassenger-statusコマンドで知ることができる。

$ sudo /usr/local/bin/passenger-status  | grep "Requests in queue"
  Requests in queue: 0

この例ではキューはたまってないことを意味する。

また下記設定を追加することで503エラーページをカスタマイズすることが可能である。

PassengerErrorOverride on
ErrorDocument 504 /error504.html

Compatibility options

PassengerResolveSymlinksInDocumentRoot

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerResolveSymlinksInDocumentRoot
Passenger-2.2以降の場合、DocumentRootがシンボリックリンクだった場合にconfig/environment.rbを探せないが、
この設定をonにすることで2.2以前の動作になるので探せるようになる。
新しいアプリケーションに関してはDocumentRootはシンボリックにしないほうがいいだろう。

PassengerAllowEncodedSlashes

http://www.modrails.com/documentation/Users%20guide%20Apache.html#_passengerallowencodedslashes_lt_on_off_gt
スラッシュがURLencodeされたリクエストを処理できるようにする。

Logging and debugging options

PassengerLogLevel

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerLogLevel
ログレベルを指定する。

  • 0: errorとwarningのみ
  • 1: important debugも表示
  • 2: debugをもっと表示
  • 3: さらにdebugを表示
PassengerDebugLogFile

http://www.modrails.com/documentation/Users%20guide%20Apache.html#_passengerdebuglogfile_lt_filename_gt
デバッグとエラーログを出力するファイルを変更できる。
デフォルトではapacheのグローバルなerrorlogになっている。

PassengerDebugger(enterprise限定)

http://www.modrails.com/documentation/Users%20guide%20Apache.html#_passengerdebugger_lt_on_off_gt
Passengerでデバッガを使うことができる。
Rails開発の場合、ローカルのmacwebrickを立ち上げて行う事が多い。その時にデバッグするのと同じようにPassengerを使っている場合でもデバッグできるようになる。
デフォルトはoff。

Phusion Passenger Enterprise: live IRB and debugging console from Phusion on Vimeo.

Advanced options

PassengerTempDir

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerTempDir
Passengerが使うtmpファイルを置くディレクトリを指定できる。
デフォルトは/tmp。
この値を変更している場合は、passenger-status等のコマンドを使う場合に環境変数でディレクトリを指定しなければならない。

export PASSENGER_TMPDIR=/my_temp-dir
sudo -E passenger-status
# The -E option tells 'sudo' to preserve environment variables.
PassengerUploadBufferDir

http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerUploadBufferDir
大きいファイルをPOSTされた場合に、Passengerはデータをファイルにバッファする。
そのディレクトリを指定する。
デフォルトはシステムのtmpディレクトリのサブディレクトリになる(PassengerTempDirを指定している場合はそのサブディレクトリ)。

Deprecated or removed options

Deprecatedなので割愛する。

まとめ

OSS版とEnterprise版があるが同じような設定で動かす事ができる、また Enterprise版にだけ有効な設定が多数ある。
Enterprise版では下記の機能が使えるようになる。

  • ゼロダウンタイムデプロイ
  • バグを含んだコードをデプロイした場合、それを検知できた場合ユーザーに表示させない機能
  • 柔軟なリソースコントロール
  • ライブデバッガー

Enterprise版ではダウンタイムなしでデプロイができるのでWebサービスなどで使う場合はこちらを選択すべきだろう、逆に個人的なWebサイトやブログならOSS版で十分である。