Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
【しくじり先生】 RailsのAutoloadingとReloadingの仕組みとやってしまったバグ
Search
Yuta Fujii
August 27, 2021
Programming
2.3k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
【しくじり先生】 RailsのAutoloadingとReloadingの仕組みとやってしまったバグ
Yuta Fujii
August 27, 2021
More Decks by Yuta Fujii
See All by Yuta Fujii
Linuxパスワードクラッキングで学べること
yutafujii0
0
7k
configの設定をちょっと変えたら Autoloading and Reloadingのエラーに 思いっきりハマってしまった話
yutafujii0
0
75
プロダクトマネジメント輪読会
yutafujii0
0
340
おさらいWebAPI
yutafujii0
0
190
F8 2019に参加したシリコンバレー訪問の感想
yutafujii0
0
240
Other Decks in Programming
See All in Programming
動作中のプログラムの中身をリアルタイムに覗く / Realtime Debugger for CSharp with Roslyn
prota
1
1.8k
Compiling a real-world CLI tool to native with Static Hermes
tmikov2023
0
100
iOS 27でニュースアプリはどう変わる!? 〜日経電子版の新機能対応と、開発事例から〜
lynnswap
7
13k
JAWS-UG 東京支部が始める、JAWS-UG支部コラボ / JAWS-UG lunchtime LT Collaboration
y0hgi
0
220
見えないものを探る要求要件定義に必要な基本的思考 / invisible-requirement-thinking
minodriven
15
8.1k
When benchmarks go bad - what I learned from measuring performance wrong
hollycummins
0
200
KiroのSpecで「五目並べ」を作ってみる
satoshi256kbyte
1
360
AWS CDKのカスタムリソースでContinuum(旧Security Agent)を実装した話 | Implementing Continuum (formerly Security Agent) via AWS CDK Custom Resource
akihisaikeda
2
140
CodeRabbitの効果検証と過ごしてみた3ヶ月
armondando
0
210
ソニーのクラウド共通基盤の変遷とAI時代の開発スタイルに合わせた進化 / The Journey of Sony’s Common Cloud Platform and Its Evolution for AI-Native Development
kenjiyoneyama
0
250
Streamlitで実現する自然言語データアプリ開発
ayumu_yamaguchi
1
350
UnityでSystem.Net.WebSocketsなWebSocketサーバが動かないのでUnity Monoのコードを覗いてみた / about implementing websocket server with unity mono
drumath2237
1
570
Featured
See All Featured
Mind Mapping
helmedeiros
1
400
Navigating Weather and Climate Data
rabernat
0
550
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
400
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
630
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
3.3k
What's in a price? How to price your products and services
michaelherold
247
13k
A Soul's Torment
seathinner
8
3.7k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.6k
sira's awesome portfolio website redesign presentation
elsirapls
0
450
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
540
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
3
1.3k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
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
ありがとうございました