🥺

Railsのconfig.eager_loadはRakeタスクには影響しない

2022/04/22に公開

何が起きたのか

タイトルの通りですが、Railsで config.eager_loadtrue にしてもRakeタスクではeager loadingが無効になります。

config/environments/production.rb
config.eager_load = true

パフォーマンスのためにそういう判断になっているようです。
Rakeタスクではすべてのクラスが必要ないことも多いと思われるのでそういうことなのでしょう。
雑に確認した範囲では少なくとも4.0から現状最新の7.0までそうなっていました。今後もきっとこの挙動は続きそうです。
https://github.com/rails/rails/blob/v7.0.2.3/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt#L9-L13

何が問題か

eager loadingされていないと何が問題なのでしょうか?
クラスがロード済みであることに依存しているロジックがある場合にうまく動かなかったりマルチスレッド処理があるとエラーになったりします。

# 名前空間下の定数全てを取りたいときとか
Foo.constants

解決するには

少々起動が遅くなったとしてもそんなに大きくないアプリケーションではeager loading出来たほうが便利なこともありますよね。
Rails 6.1以降では config.rake_eager_load を設定することで解決できます。

config.rake_eager_load = true

https://github.com/rails/rails/blob/v7.0.2.3/guides/source/configuring.md#configrake_eager_load

これを読んでいる賢明なる皆様はきっとRails 6.1未満という化石を使っていることはないと思うのですが、Rails 6.1未満でどうすればよいかという案をいくつか書いておきます。

  1. Rails 6.1以上にアップデートして config.rake_eager_load を設定する
  2. 必要なRakeタスクで Rails.application.eager_load! を呼ぶ
  3. 必要なクラスを自前でロードする
  4. 以下のようなフックを仕込んで config.rake_eager_load を再現する
    config/application.rb
    # 以下はRails 6.1未満で `rake_eager_load` を疑似的に再現するための実装
    config.rake_eager_load = nil
    rake_tasks do
      if Rails.gem_version >= Gem::Version.new("6.1.0")
        raise "Rails 6.1以降では不要なのでこのコードを削除してください"
      end
      
      config.after_initialize do
        self.eager_load! if self.config.rake_eager_load
      end
    end
    
    必要な環境で有効化する
    config/environments/production.rb
    config.rake_eager_load = true
    

まとめ

今回は4の方法で回避しました。
Railsのバージョンアップしようね。

Discussion