ドキュメントを読み込むのは大事、ということでRailsガイドを頭から読んでいく取り組みをしています。 各章ごとに、(Railsガイドにちゃんと書いてあるのに)知らなかった機能を雑にまとめていきます。
今回は、Rails のスレッドとコード実行の章です。
ExecutorとReloader
リンクはこちら
理解するのが難しかった(今でも完全には理解できていない)ですが、Kaigi on Rails 2023の動画がわかりやすかったです。
- Executorはフレームワークとアプリケーションのコードを区別するもの
- これがあるのでDBコネクションやキャッシュクリアを意識せずに開発できる
- 例えばアクションを実行する際、Executorは、実行前に
to_run
を、実行後にto_complete
を呼び出す - Executorはリソース管理のみ
- ReloaderはExecutorをwrapした上で、アプリケーションコードを実行する前に最新のコードが読まれているかを確認する
- 例えば、gemからアプリケーションコードを読むとき、リソース管理とコードの再読み込みをするには、
Rails.application.reloader.wrap
を使う
permit_concurrent_loads
リンクはこちら
Railsガイドのサンプルコードの理解
Rails.application.executor.wrap do th = Thread.new do Rails.application.executor.wrap do User # 内側のスレッドはここで待機する # 他のスレッドが実行中はUserを読み込めない end end th.join # 外側のスレッドは'running'ロックをつかんだままここで待機する end
- 「外側のスレッド」: 1行目の
Rails.application.executor.wrap
を実行している - 「内側のスレッド」: Thread.newによって生成された新たなスレッド
join
: 特定のスレッド(今回はth)の実行が完了するまで、それを呼び出したスレッド(外側のスレッド)の実行を一時停止する
=> Userを読み込むためには外側のrunning
ロックが解放されないといけないが、外側のスレッドはth.join
で内側のスレッドの終了を待って一時停止しているのでデッドロックになる
これに対して、permit_concurrent_loads
を使うことでそのスレッドでは、提供されたブロック内で自動読み込みされた可能性のある定数を参照解決しないことが保証され、ある意その定数の読み取りを他スレッドに「許可」できる。