Railsガイドにきちんと目を通して新しい知識を得る - Rails のスレッドとコード実行編 -

ドキュメントを読み込むのは大事、ということでRailsガイドを頭から読んでいく取り組みをしています。 各章ごとに、(Railsガイドにちゃんと書いてあるのに)知らなかった機能を雑にまとめていきます。

今回は、Rails のスレッドとコード実行の章です。

railsguides.jp

ExecutorとReloader

リンクはこちら

理解するのが難しかった(今でも完全には理解できていない)ですが、Kaigi on Rails 2023の動画がわかりやすかったです。

kaigionrails.org

  • 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を使うことでそのスレッドでは、提供されたブロック内で自動読み込みされた可能性のある定数を参照解決しないことが保証され、ある意その定数の読み取りを他スレッドに「許可」できる。