Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
【しくじり先生】 RailsのAutoloadingとReloadingの仕組みとやってしまったバグ
Search
Yuta Fujii
August 27, 2021
Programming
2
2k
【しくじり先生】 RailsのAutoloadingとReloadingの仕組みとやってしまったバグ
Yuta Fujii
August 27, 2021
Tweet
Share
More Decks by Yuta Fujii
See All by Yuta Fujii
Linuxパスワードクラッキングで学べること
yutafujii0
0
6.6k
configの設定をちょっと変えたら Autoloading and Reloadingのエラーに 思いっきりハマってしまった話
yutafujii0
0
48
プロダクトマネジメント輪読会
yutafujii0
0
310
おさらいWebAPI
yutafujii0
0
180
F8 2019に参加したシリコンバレー訪問の感想
yutafujii0
0
200
Other Decks in Programming
See All in Programming
CursorはMCPを使った方が良いぞ
taigakono
1
170
ASP.NETアプリケーションのモダナイズ インフラ編
tomokusaba
1
410
Cline指示通りに動かない? AI小説エージェントで学ぶ指示書の書き方と自動アップデートの仕組み
kamomeashizawa
1
570
Julia という言語について (FP in Julia « SIDE: F ») for 関数型まつり2025
antimon2
3
980
iOSアプリ開発で 関数型プログラミングを実現する The Composable Architectureの紹介
yimajo
2
210
Team topologies and the microservice architecture: a synergistic relationship
cer
PRO
0
1k
Is Xcode slowly dying out in 2025?
uetyo
1
190
「Cursor/Devin全社導入の理想と現実」のその後
saitoryc
0
140
プロダクト志向なエンジニアがもう一歩先の価値を目指すために意識したこと
nealle
0
110
Bytecode Manipulation 으로 생산성 높이기
bigstark
2
370
データの民主化を支える、透明性のあるデータ利活用への挑戦 2025-06-25 Database Engineering Meetup#7
y_ken
0
310
Haskell でアルゴリズムを抽象化する / 関数型言語で競技プログラミング
naoya
17
4.9k
Featured
See All Featured
GitHub's CSS Performance
jonrohan
1031
460k
Building an army of robots
kneath
306
45k
How GitHub (no longer) Works
holman
314
140k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
50k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
30
2.1k
[RailsConf 2023] Rails as a piece of cake
palkan
55
5.6k
Optimizing for Happiness
mojombo
379
70k
Building Better People: How to give real-time feedback that sticks.
wjessup
367
19k
The Cost Of JavaScript in 2023
addyosmani
51
8.4k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
45
7.4k
A Modern Web Designer's Workflow
chriscoyier
693
190k
Designing for Performance
lara
609
69k
Transcript
【しくじり先生】 RailsのAutoloadingとReloadingの仕組み とやってしまったバグ 2021.08.27
@__yutafujii__ • 定数のAutoloadingとReloadingの仕組み • 開発環境でのサーバー処理 • しくじりました話 Contents
@__yutafujii__ • HR領域に関連するWebシステム • 2019年に入社して開発・運用を行っている • 当時のRailsのバージョンは5.2 • しばらくしてフロントをVueで書くようになった 前提知識
class Dog < Animal
いきなりですが
@__yutafujii__ このRubyコードを実行すると...
@__yutafujii__ →知らない定数があるとエラーが出る
@__yutafujii__ 事前にそれら定数が書かれているファイルをrequireする
@__yutafujii__ • このコードはなぜ動くのか なぜRailsはrequireを書かなくていいのか?
もうひとつ
@__yutafujii__ なぜRailsはファイル修正したらすぐ反映されるのか?
@__yutafujii__ 今日はこの話を... https://railsguides.jp/autoloading_and_reloading_constants.html
Zeitwerk Classic →
@__yutafujii__ Autoloading / Reloadingの方法は過渡期にある Classic Classic Zeitwerk Zeitwerk <= 5.2
6.0 / 6.1 7.0 (deprecated)
Zeitwerk
ZeitwerkモードのAutoloading
loader = Zeitwerk::Loader.new loader.push_dir(...) # ...にautoload_paths loader.setup # ready!
def define(parent, cname, abspath) parent.autoload(cname, abspath) cref = [parent, cname]
c2a[cref] = abspath a2c[abspath] = cref end
None
[Object, :User] => "/Users/fxn/blog/app/models/user.rb" [Object, :Hotel] => "/Users/fxn/blog/app/models/hotel"
@__yutafujii__ 最初にディレクトリを探索して 定数と定義ファイルをHashで保持しているから なぜRailsはrequireを書かなくていいのか? Answer
ZeitwerkモードのReloading
ActiveSupport::FileUpdateChecker
Rails::Application::Finisher
autoloaders.main.reload # Zeitwerk::Loaderインスタンス
def unload_autoload(parent, cname) parent.__send__(:remove_const, cname) end
@__yutafujii__ Answer なぜRailsはファイル修正したらすぐ反映されるのか? ファイル修正を検知した場合, 前述のHashテーブルを再更新
ちなみにRails 5.2までは Classic こ う だ っ た
@__yutafujii__ 未知の定数に出会った時, それがありそうなファイルパスを探して読み込むから Autoloading Answer 実際には autoload_path に入っているパス Classic
@__yutafujii__ Answer rails server ファイル修正を検知した場合, 修正が加わったクラスやモジュールを定数リストから落とし 次のリクエスト受理時にAutoloadingを再度走らせるから Reloading Classic
@__yutafujii__ RailsはAutoloadingとReloadingのために何をしたのか? 未知の定数に遭遇 → NameError まず推論 ファイル修正 → 反映されない 必要に応じてリロード Autoloading Reloading
Classic
@__yutafujii__ RailsはAutoloadingとReloadingのために何をしたのか? 未知の定数に遭遇 → NameError まず推論 ファイル修正 → 反映されない 必要に応じてリロード • 既にある処理を変えている:メソッドのオーバーライト
◦ Moduleクラスのconst_missing Autoloading Classic
@__yutafujii__ RailsはAutoloadingとReloadingのために何をしたのか? ActiveSupport::Dependencies Classic
@__yutafujii__ RailsはAutoloadingとReloadingのために何をしたのか? 未知の定数に遭遇 → NameError まず推論 ファイル修正 → 反映されない 必要に応じてリロード • これまでにない処理がある:メソッドの追加
◦ consoleでのreload!メソッド ◦ serverでのreload & autoloadのメカニズム(後述) Reloading Classic
@__yutafujii__ RailsはAutoloadingとReloadingのために何をしたのか? ActiveSupport::FileUpdateChecker Classic
@__yutafujii__ RailsはAutoloadingとReloadingのために何をしたのか? Rails::Application::Finisher Classic
開発環境でのサーバーの挙動
@__yutafujii__ 開発環境サーバーのレスポンス 受理 HTTP POST /admin/books_tags
@__yutafujii__ 開発環境サーバーのレスポンス 受理 routing namespace :admin do resources :books_tags end
@__yutafujii__ 開発環境サーバーのレスポンス 受理 routing controller 特定 controller_name = ‘admin/books_tags_controller’ controller_name.constantize
Classic
@__yutafujii__ 開発環境サーバーのレスポンス 受理 routing controller 特定 controller_name = ‘admin/books_tags_controller’ controller_name.constantize
# => Admin::BooksTagsController const_missing発火 Classic
@__yutafujii__ 開発環境サーバーのレスポンス 受理 routing controller 特定 処理 module Admin class
BooksTagsController def create @tagging = BookTag.new # … end end end Classic
@__yutafujii__ 開発環境サーバーのレスポンス 受理 routing controller 特定 処理 module Admin class
BooksTagsController def create @tagging = BookTag.new # … end end end const_missing発火 Classic
@__yutafujii__ 開発環境サーバーのレスポンス 受理 routing controller 特定 処理 レスポンス 200 OK
POST /admin/books_tags
作業してファイル修正すると...
@__yutafujii__ ファイルに変更が加わっているとき 受理 routing controller 特定 処理 レスポンス 修正ファイルをunload unload対象の定数もリストから削除
Classic
@__yutafujii__ ファイルに変更が加わっているとき 受理 routing controller 特定 処理 レスポンス const_missing const_missing
Autoloading Autoloading Classic
@__yutafujii__ Answer Reloadingの仕組み Classic
設定ミスによりAPI開発で しくじってしまった...
ある日,普通に開発していると...
@__yutafujii__ 突如現れるエラー A copy of XXXXXX has been removed from
the module tree....
@__yutafujii__ 対処方法として見つかったもの① https://tech.unifa-e.com/entry/2017/08/09/183519 ググって見つかったブログより抜粋
@__yutafujii__ すると,次第に「なぜそこで起きている?!」という場所でこのエラーが登場するように なる レベルアップしていくエラー Api::One::Parent::ChildController
@__yutafujii__ 対処方法として見つかったもの② http://sugilog.hatenablog.com/entry/20110806/1312584149 ググって見つかったブログより抜粋 config.cache_class = true
@__yutafujii__ 対処方法として見つかったもの② config/development.rb
@__yutafujii__ この設定をすると,ソースコードを書き換えた時に変更が反映されなくなる Reloadingが効かない しかし
本当の原因
@__yutafujii__ 本当の原因 config.reload_classes_only_on_change = false
@__yutafujii__ 本当の原因
@__yutafujii__ 本当の原因 config.reload_classes_only_on_change = false の場合 リスエスト処理が完了するたびに 次のリクエストで都度定数を全削除してしまう
@__yutafujii__ リクエストの都度Reloadingされる 受理 routing controller 特定 処理 レスポンス const_missing Autoloading
Classic remove_const
その結果, リクエストが同時に飛んでくると
@__yutafujii__ リクエストが同時に飛んでくると 受理 routing controller 特定 処理 レスポンス const_missing Autoloading(1)
Classic remove_const(1) 受理 routing controller 特定 処理 レスポンス const_missing Autoloading(2) remove_const(2)
@__yutafujii__ 実際に起きていたこと GET /api/books GET /api/tags Vueインスタンス created() controller 特定
controller 特定 Subdomain::Api::BooksController Subdomain::Api::TagsController
@__yutafujii__ 実際に起きていたこと GET /api/books GET /api/tags Vueインスタンス created() Subdomain::Api::BooksController Subdomain::Api::TagsController
Race Conditionの発生 controller 特定 controller 特定
片方が Subdomain Subdomain::Api Subdomain::Api::BooksController を読み込む間にもう片方が remove_const する
同一Class/Moduleのオブジェクトが 2個できてしまった Subdomain::Api
@__yutafujii__ 実際に起きていたこと
@__yutafujii__ Inflector.constantize(from_mod_name).equal?(from_mod) 実際に起きていたこと
全ては3年前に始まっていた
@__yutafujii__ 3年前の2018年3月に追加されていた
ところでこのrace conditionって
@__yutafujii__ イシューは上がっていたがそれ自体は解決されなかった https://github.com/rails/rails/issues/33209
@__yutafujii__ イシューは上がっていたがそれ自体は解決されなかった https://github.com/rails/rails/issues/33209
@__yutafujii__ • 新たなautoloadの方法 登場するZeitwerk https://github.com/fxn/zeitwerk
@__yutafujii__ Classicモードはいずれ使えなくなる • Zeitwerkモードに早めに移行しておこう https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#classic-mode-is-deprecated
@__yutafujii__ 移行方法 • 実際にはテストが大量にこけたりしたが無事に移行済み https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#enabling-zeitwerk-mode
@__yutafujii__ Classicモードはいずれ使えなくなる https://github.com/rails/rails/commit/0d523d83657ce7066f25d87f6f094e804590e1e8ants.html#classic-mode-is-deprecated
85 828 → ActiveSupport::Dependencies
ありがとうございました
余談
@__yutafujii__ 本当の原因
@__yutafujii__ Application(選考)というModelを使っていたため Answer この設定が必要だった背景(推測)
@__yutafujii__ どうしても「選考」というデータが重要であり,自然な英訳で「Application」というモデル が作成された しかし,Applicationという名前のクラスはRailsのアプリケーション作成時に一つ作成さ れる HRに関するシステムだったので
@__yutafujii__ この設定が必要だった背景(推測) config/application.rb
@__yutafujii__ config/application.rbとapp/models/application.rb ソースコードでApplicationが使われている箇所では,app/models/application.rbをうま くautoloadしてくれなかった
@__yutafujii__ そうしたわけで,こうした設定が追加されていた
@__yutafujii__ requireしてしまうとreloadされない Never be require d https://guides.rubyonrails.org/autoloading_and_reloading_constants_classic_mode.html#autoloading-and-require
@__yutafujii__ だからこの設定がさらに追加されたのだろう
@__yutafujii__ その結果
@__yutafujii__ • 定数のAutoloading/Reloadingは変わった • Classicモードだったら早めに移行しておこう • ソースコードの勉強になった • きっと誰もしくじらないだろう まとめ
@__yutafujii__ 参考 RAILS GUIDES https://guides.rubyonrails.org/autoloading_and_reloading_constants_classic_mode.html https://guides.rubyonrails.org/autoloading_and_reloading_constants.html Rails GitHub https://github.com/rails/rails/blob/main/activesupport/lib/active_support/dependencies.rb https://github.com/rails/rails/blob/main/activesupport/lib/active_support/inflector/methods.rb
Rails Issue https://github.com/rails/rails/issues/33209 Zeitwerk GitHub https://github.com/fxn/zeitwerk#pronunciation バグ検証メモ https://zenn.dev/yutafujii/scraps/cd5500cd468a39
ありがとうございました