[注意]この記事はRails 4.2.6について書かれている。Rails 5では大きく変わっている。
今、とあるRails 4プロジェクトでproductionでも
config.cache_classes = false
にして運用している。つまりdevelopmentと同様に、ソースファイルに変更を加えると、次回のリクエストのときに自動的に再読込される。
もちろん最初はパフォーマンス低下を心配したが、測定してみると数ミリ秒の差もなかったのでこれで数ヶ月運用を続けている。
今回ふと思い立って、このオートロードの仕組みを調べてみた。
基本的にはRailsじゃなくてもActiveSupportの自動require機能を使う - 昼メシ物語にも書かれているように、const_missing
をフックしてファイルを読み込んでいる。
しかし、2回目以降のリクエストの場合はすでにクラスが定義されていてconst_missing
は発生しないはずである。そこはどうやっているのだろうか?
結論から言うと、オートロードで読み込んだファイルのリストを記録しておき、リクエストのときにそのいずれかに変更があればremove_unloadable_constants!
で定数定義を削除している。それによってconst_missing
が発生するようになる。
またconfig.cache_classes = false
の場合はActiveSupport::Dependencies.mechanism == :load
になり、require
でなくload
でファイル読み込みをするようになる。
[41] pry(main)> ActiveSupport::Dependencies.mechanism
=> :load
呼び出し関係はこのようになる:
ActionDispatch::Reloader(Rack ミドルウェア)
ActiveSupport::FileUpdateChecker#execute
ActiveSupport::Dependencies.clear
ActiveSupport::Dependencies.remove_unloadable_constants!
以上の理解が正しければ、
- リクエストがあったときにオートロード対象のいずれかのファイルに変更があれば、それら全てがクリアされる。したがって、
const_missing
が発生し、遅延が生じる可能性がある(以降のリクエストも含めて)。 - どのファイルにも変更がなければ、ファイルのタイムスタンプをチェックするだけなので、ほぼ遅延は生じない。
実験してみたらその通りになった。
参考
Rails autoloading — how it works, and when it doesn't
auto_loadは便利さと引き換えに複雑さとトラブルをもたらすという話
Tracing the Rails Autoloader
log_activity
などauto_loadのデバッグに便利なあれこれ
[1] pry(main)> ActiveSupport::Dependencies.log_activity = true
[2] pry(main)> ActiveSupport::Dependencies.logger = Logger.new(STDOUT)