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
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Yuta Fujii
August 27, 2021
Programming
2
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.9k
configの設定をちょっと変えたら Autoloading and Reloadingのエラーに 思いっきりハマってしまった話
yutafujii0
0
52
プロダクトマネジメント輪読会
yutafujii0
0
320
おさらいWebAPI
yutafujii0
0
180
F8 2019に参加したシリコンバレー訪問の感想
yutafujii0
0
210
Other Decks in Programming
See All in Programming
Fluid Templating in TYPO3 14
s2b
0
120
16年目のピクシブ百科事典を支える最新の技術基盤 / The Modern Tech Stack Powering Pixiv Encyclopedia in its 16th Year
ahuglajbclajep
5
980
なぜSQLはAIぽく見えるのか/why does SQL look AI like
florets1
0
440
Data-Centric Kaggle
isax1015
2
760
CSC307 Lecture 03
javiergs
PRO
1
490
Grafana:建立系統全知視角的捷徑
blueswen
0
320
Amazon Bedrockを活用したRAGの品質管理パイプライン構築
tosuri13
2
170
Architectural Extensions
denyspoltorak
0
270
AIと一緒にレガシーに向き合ってみた
nyafunta9858
0
140
余白を設計しフロントエンド開発を 加速させる
tsukuha
7
2.1k
IFSによる形状設計/デモシーンの魅力 @ 慶應大学SFC
gam0022
1
290
MUSUBIXとは
nahisaho
0
120
Featured
See All Featured
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
55
How to Talk to Developers About Accessibility
jct
2
120
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
580
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
50
Build The Right Thing And Hit Your Dates
maggiecrowley
38
3k
Exploring anti-patterns in Rails
aemeredith
2
240
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
ラッコキーワード サービス紹介資料
rakko
1
2.2M
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
170
Thoughts on Productivity
jonyablonski
74
5k
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
0
100
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
88
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
ありがとうございました