2021-06-11 Rails とマルチスレッド
Railsガイドにマルチスレッドの扱い方に関して記述がある。
Rails のスレッドとコード実行 - Railsガイド
各スレッドは、アプリケーションのコードを実行する前にこのようにラップされるべきです。これにより、アプリケーションで何らかの作業を他のスレッドに手動で委譲する場合(Thread.newを使うなど)や、Concurrent Rubyのスレッドプールを用いる場合は、そのブロックをただちに以下のようにラップすべきです。
Thread.new do
Rails.application.executor.wrap do
# ここにコードを書く
end
end
デフォルトのRailsアプリケーションでは、Executorのコールバックを以下の用途に用います。
- 自動読み込みや再読み込みを安全に行えるスレッドがどれかをトラッキングする
- Active Recordクエリキャッシュをオン/オフする
- 獲得したActive Recordコネクションをコネクションプールに返す
- 内部キャッシュの寿命を制限する
外側のスレッドは
permit_concurrent_loads
メソッドを呼び出すことでこのデッドロックを防げます。このメソッドを呼ぶと、提供されたブロック内で自動読み込みされた可能性のある定数をそのスレッドが参照解決しないことが保証されます。この保証に合致する最も安全な手法は、このメソッド呼び出しを、可能な限りブロッキングされる呼び出しの近くに配置することです。
Rails.application.executor.wrap do
th = Thread.new do
Rails.application.executor.wrap do
User # 内側のスレッドは'load'ロックを取得し
# Userを読み込んで続行できる
end
end
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
th.join # 外側のスレッドはここで待機するがロックを保持しない
end
end