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
configの設定をちょっと変えたら Autoloading and Reloadingのエラ...
Search
Yuta Fujii
May 21, 2021
0
43
configの設定をちょっと変えたら Autoloading and Reloadingのエラーに 思いっきりハマってしまった話
configの設定をちょっと変えたら
Autoloading and Reloadingのエラーに
思いっきりハマってしまった話
Yuta Fujii
May 21, 2021
Tweet
Share
More Decks by Yuta Fujii
See All by Yuta Fujii
【しくじり先生】 RailsのAutoloadingとReloadingの仕組みとやってしまったバグ
yutafujii0
2
1.9k
Linuxパスワードクラッキングで学べること
yutafujii0
0
6.1k
プロダクトマネジメント輪読会
yutafujii0
0
300
おさらいWebAPI
yutafujii0
0
170
F8 2019に参加したシリコンバレー訪問の感想
yutafujii0
0
190
Featured
See All Featured
Done Done
chrislema
181
16k
Building Applications with DynamoDB
mza
90
6.1k
The Art of Programming - Codeland 2020
erikaheidi
52
13k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
28
9.1k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
27
4.2k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
7
570
Large-scale JavaScript Application Architecture
addyosmani
510
110k
RailsConf 2023
tenderlove
29
900
Documentation Writing (for coders)
carmenintech
65
4.4k
Optimising Largest Contentful Paint
csswizardry
33
2.9k
Building Flexible Design Systems
yeseniaperezcruz
327
38k
Building an army of robots
kneath
302
42k
Transcript
configの設定をちょっと変えたら Autoloading and Reloadingのエラーに 思いっきりハマってしまった話 2021.05.20
Yuta Fujii • RailsのAutoloading and Reloading Constantsとは • development環境でのサーバー処理と定数読み込み •
開発中に遭遇したエラーの話 • 直近の動向 Contents
Yuta Fujii • 人材紹介業に関連するシステム • 2019年夏に入社して開発・運用を行っている • 入社当時,プレーンはRailsアプリケーションとして使っていた.画面もerbで記述し ていた •
当時のRailsのバージョンは5.2 前提知識
Yuta Fujii Autoloading and Reloading Constants
いきなりですが
Yuta Fujii このRubyコードはエラーを起こす?
Yuta Fujii →知らない定数があるとエラーが出る
Yuta Fujii 事前にそれら定数が書かれているファイルをrequireする
Yuta Fujii • このコードはなぜ動くのか なぜRailsはrequireを書かなくていいのか?
Yuta Fujii 未知の定数に出会った時, それがありそうなファイルパスを探して読み込むから なぜRailsはrequireを書かなくていいのか? Answer 実際にはautoload_pathに入っているパス
もうひとつ
Yuta Fujii • modelやcontrollerなどを修正してもブラウザでリロードすると反映される なぜRailsはファイル修正したらすぐ反映されるのか?
Yuta Fujii (rails serverにおいて) ファイル修正を検知した場合, 修正が加わったクラスやモジュールを定数リストから落とし 次のリクエスト受理時にAutoloadingを再度走らせるから なぜRailsはファイル修正したらすぐ反映されるのか? Answer
Yuta Fujii Autoloading and Reloading Constants 未知の定数に遭遇 → NameError まず推論 ファイル修正 →
反映されない 必要に応じてリロード
Yuta Fujii Autoloading and Reloading Constants 未知の定数に遭遇 → NameError まず推論 ファイル修正 →
反映されない 必要に応じてリロード Autoloading Reloading
RailsはAutoloadingとReloadingのため に何をしたのか?
Yuta Fujii RailsはAutoloadingとReloadingのために何をしたのか? 未知の定数に遭遇 → NameError まず推論 ファイル修正 → 反映されない 必要に応じてリロード Autoloading
Reloading
Yuta Fujii RailsはAutoloadingとReloadingのために何をしたのか? 未知の定数に遭遇 → NameError まず推論 ファイル修正 → 反映されない 必要に応じてリロード Autoloading
• 既にある処理を変えている=メソッドのオーバーライト ◦ Moduleクラスのconst_missing
Yuta Fujii RailsはAutoloadingとReloadingのために何をしたのか? ActiveSupport::Dependencies
Yuta Fujii RailsはAutoloadingとReloadingのために何をしたのか? 未知の定数に遭遇 → NameError まず推論 ファイル修正 → 反映されない 必要に応じてリロード Reloading
• これまでにない処理がある=メソッドの追加 ◦ consoleでのreload!メソッド ◦ Serverでのreload & autoloadのメカニズム(後述)
Yuta Fujii RailsはAutoloadingとReloadingのために何をしたのか? ActiveSupport::FileUpdateChecker
Yuta Fujii RailsはAutoloadingとReloadingのために何をしたのか? Rails::Application::Finisher
今日は特にdevelopment環境での サーバーの挙動を考えます
Yuta Fujii 開発環境でブラウザからアプリを開く時,何が起きている? ClientがHTTPリクエスト HTTP POST /admin/books_tags
Yuta Fujii namespace :admin do resources :books_tags end 開発環境でブラウザからアプリを開く時,何が起きている? ClientがHTTPリクエスト
routingがあるか探索
Yuta Fujii controller_name = ‘admin/books_tags_controller’ controller_name.constantize # => Admin::BooksTagsController 開発環境でブラウザからアプリを開く時,何が起きている?
ClientがHTTPリクエスト routingがあるか探索 対応するコントローラーを探索
Yuta Fujii module Admin class BooksTagsController def create @tagging =
BookTag.new # … end end end 開発環境でブラウザからアプリを開く時,何が起きている? ClientがHTTPリクエスト routingがあるか探索 対応するコントローラーを探索 処理が実行,レスポンス
Yuta Fujii 開発環境でブラウザからアプリを開く時,何が起きている? ClientがHTTPリクエスト routingがあるか探索 対応するコントローラーを探索 処理が実行,レスポンス 終了 200 OK
POST /admin/books_tags
定数に着目して もっとよく見直してみる
Yuta Fujii Autoloadingの仕組み ClientがHTTPリクエスト HTTP POST /admin/books_tags
Yuta Fujii namespace :admin do resources :books_tags end Autoloadingの仕組み ClientがHTTPリクエスト
routingがあるか探索
Yuta Fujii controller_name = ‘admin/books_tags_controller’ controller_name.constantize # => Admin::BooksTagsController Autoloadingの仕組み
ClientがHTTPリクエスト routingがあるか探索 対応するコントローラーを探索 ない場合は const_missingメソッド発火 autoload_pathから定義を探索 ”Admin::BooksTagsController” 定数を取得しようとする
Yuta Fujii module Admin class BooksTagsController def create @tagging =
BookTag.new # … end end end Autoloadingの仕組み ClientがHTTPリクエスト routingがあるか探索 対応するコントローラーを探索 処理が実行,レスポンス ”BookTag” 定数を取得しようとする ない場合は const_missingメソッド発火 autoload_pathから定義を探索
Yuta Fujii Autoloadingの仕組み ClientがHTTPリクエスト routingがあるか探索 対応するコントローラーを探索 処理が実行,レスポンス 終了 200 OK
POST /admin/books_tags
Yuta Fujii Reloadingの仕組み ClientがHTTPリクエスト routingがあるか探索 対応するコントローラーを探索 処理が実行,レスポンス 終了 修正ファイルをunload unload対象の定数もリストから削除
ない場合は const_missingメソッド発火 autoload_pathから定義を探索 ”Admin::BooksTagsController” 定数を取得しようとする ”BookTag” 定数を取得しようとする ない場合は const_missingメソッド発火 autoload_pathから定義を探索 先に定数リストから修正入ったものを削除しておくことで const_missingを発火する ようにしておく
だからRailsでは Require書かなくても動くものが多い だからRailsでは ファイル修正がすぐ反映される
定数にまつわる設定ミスで 実際にあった問題
Yuta Fujii ある日,普通に開発していると... 突如現れるエラー
Yuta Fujii 突如現れるエラー
Yuta Fujii 突如現れるエラー 画面のイメージ
Yuta Fujii 正直メッセージの内容がわかりづらい “A copy of Foo has been removed
from the module tree but is still active!” どゆこと? ME “A copy of Foo has been removed from the module tree but is still active!” ... ME
Yuta Fujii 対処方法として見つかったもの① https://tech.unifa-e.com/entry/2017/08/09/183519 ググって見つかったブログより抜粋
Yuta Fujii 1個直すとまた1個別の場所で同じエラーが出たりする ただ,全部のクラスやモジュールで生じているわけではなかった 根本解決している気はしなかったが,しばらくこのまま運用を続けた そうしている間にVueを導入したり外部APIを実装するなどソースコードも複雑になって いった しかし
Yuta Fujii すると,次第に「なぜそこで起きている?!」という場所でこのエラーが登場するように なる これまではモデルの読み込みエラーだけだったが,コントローラー名称の一部分でもエ ラーが起こるように レベルアップしていくエラー
Yuta Fujii レベルアップしていくエラー “A copy of Foo::Api has been removed
from the module tree but is still active!” え,君も!?てかこれどこ? ME “A copy of Foo::Api::Some has been removed from the module tree but is still active!” さっきと微妙に違う... ME
Yuta Fujii 対処方法として見つかったもの② http://sugilog.hatenablog.com/entry/20110806/1312584149 ググって見つかったブログより抜粋
Yuta Fujii 対処方法として見つかったもの② configを変更すれば問題は回避できた
Yuta Fujii この設定をすると,ソースコードを書き換えた時に変更が反映されなくなる ファイル修正の都度サーバーを再起動する必要が生じた しかし
Yuta Fujii 次第にチーム全員が諦観し,疲弊していく 誰でも良い。早く直してくれ。
起きていたことと本当の原因
Yuta Fujii 本当の原因
Yuta Fujii config.reload_classes_only_on_change = false の場合 reload_classes_only_on_change = falseだと ClientがHTTPリクエスト
routingがあるか探索 対応するコントローラーを探索 処理が実行,レスポンス 終了 リスエスト処理が完了するたびに 次のリクエストで都度定数を全削除してしまう
Yuta Fujii その結果 ClientがHTTPリクエスト routingがあるか探索 対応するコントローラーを探索 ClientがHTTPリクエスト routingがあるか探索 対応するコントローラーを探索 GET
/api/books GET /api/tags Vueインスタンス created() ほぼ同時にリクエストが 2本送られる
Yuta Fujii その結果 ClientがHTTPリクエスト routingがあるか探索 対応するコントローラーを探索 ClientがHTTPリクエスト routingがあるか探索 対応するコントローラーを探索 GET
/api/books GET /api/tags Vueインスタンス created() ほぼ同時にリクエストが 2本送られる Subdomain::Api::BooksController Subdomain::Api::TagsController remove_const remove_const config.reload_classes_only_on_change = false なのでリクエストの度にここでほぼ全ての定数が削除
Yuta Fujii その結果 → race condition ClientがHTTPリクエスト routingがあるか探索 対応するコントローラーを探索 ClientがHTTPリクエスト
routingがあるか探索 対応するコントローラーを探索 GET /api/books GET /api/tags Vueインスタンス created() ほぼ同時にリクエストが 2本送られる Subdomain::Api::BooksController Subdomain::Api::TagsController remove_const remove_const Subdomain::Api::BooksControllerが missing_constを引き起こし 順次Moduleオブジェクトを作っていく. (Subdomain, Subdomain::Api, Subdomain::Api::BooksController) 各Moduleオブジェクト作成途中に 2本目のリクエストで必要な Subdomain::Api::BooksControllerが missing_constを引き起こし, 同じModuleのオブジェクトが2個できてしまう
Yuta Fujii その結果 → race condition
全ては3年前に始まっていた
Yuta Fujii 3年前の2018年3月に追加されていた
Yuta Fujii Application(選考)というModelを使っていたため Answer この設定が必要だった背景(推測)
Yuta Fujii どうしても「選考」というデータが重要であり,自然な英訳で「Application」というモデル が作成された しかし,Applicationという名前のクラスはRailsのアプリケーション作成時に一つ作成さ れる 人材紹介に関するシステムだったので
Yuta Fujii この設定が必要だった背景(推測) config/application.rb
Yuta Fujii config/application.rbとapp/models/application.rb ソースコードでApplicationが使われている箇所では,app/models/application.rbをうま くautoloadしてくれなかった
Yuta Fujii そうしたわけで,こうした設定が追加されていた
Yuta Fujii requireしてしまうとreloadされない Never be require d https://guides.rubyonrails.org/autoloading_and_reloading_constants_classic_mode.html#autoloading-and-require
Yuta Fujii だからこの設定がさらに追加されたのだろう
Yuta Fujii その結果
Yuta Fujii Applicationモデルのロードエラーの防ぎ方 require_dependencyを利用.もっとも最後の手段的な位置付け https://guides.rubyonrails.org/autoloading_and_reloading_constants_classic_mode.html#require-dependency
ところでこのrace conditionって
Yuta Fujii Railsの設計そのものがあまりイケテナイ? • Railsでもイシューが上がっている • 対応案も出されていたが,Rubyも改修が必要だった • しかし,成就することはなかった https://github.com/rails/rails/issues/33209
Yuta Fujii • 新たなautoloadの方法 登場するZeitwerk https://github.com/fxn/zeitwerk
Yuta Fujii Classicモードはいずれ使えなくなる • Zeitwerkモードに早めに移行しておこう https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#classic-mode-is-deprecated
Yuta Fujii 移行方法は1行configに追加するだけではある • 実際にはテストが大量にこけたりしたが無事に移行済み https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#enabling-zeitwerk-mode
Yuta Fujii 余談 Dog オブジェクト Object オブジェクト Class オブジェクト Is
instance of Is instance of Is inherited from name: ‘Object’ constants:[:Dog, ..etc.] 定数とクラスと名前の話. 右の1行がインタプリタによって処理されるとき, Classクラスのインスタンスである Dogクラスオブ ジェクトが作成される. 基本的に作成されるクラスは Objectクラスを継承 しており,Dogクラスも同様となる. Objectクラスのconstantsに :Dogが追加される. 定数DogにはDogクラスのオブジェクトが対応す る. constantとしてのDogと,Classクラスのオブジェク トであるDogクラスと,Dogクラスというオブジェクト に備わっているnameメソッド(Moduleクラスのメ ソッド)の戻り値になる ’Dog’の区別を意識するの はなかなか難しい. Is instance of name: ‘Dog’ constants:[] name: ‘Class’ constants:[]
Yuta Fujii 参考 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
ありがとうございました