Rails 7: Active RecordのConnectionPoolsがFiberセーフになった(翻訳)
パラレリズムとコンカレンシーは、Ruby 3の大きな焦点のひとつです。この分野の先駆者といえばRubyのFiber
です。Fiberは、実行の一時停止、ループ、再開を可能にするコンカレンシーメカニズムであり、コンテキストスイッチのコストが非常に小さくなります。開発者は、スレッドより少ないメモリ消費でコードセグメントの実行を制御できるようになります。
Fiberは、スケーラブルでノンブロッキングのクライアント=サーバーに最適のソリューションですが、RailsでFiberを利用できるようになるまでしばらく時間を要しました。
変更前
RailsでスレッドからFiberへの置き換えが進めば、パフォーマンスの向上が見込まれます。そのひとつがActive Recordです。Active Supportのconfig.active_support.isolation_level
で分離レベルを設定したとしても、現在のActive Recordは未だにスレッドに依存しています。分離レベルは、データベースのトランザクションが他のユーザーやシステムに伝搬する方法を決定します。
分離レベルが重要な理由を知るには、Active RecordのConnectionPool
について知っておく必要があります。コネクションプールは、Active Recordの複数のコネクションを管理します。Railsはこのトランザクションを実行するために、I/O操作をスレッドに逃し、各スレッドが個別のDBコネクションを維持します(Pumaの場合)。そうしないとデータベースとの個別のやりとりが混ざってしまう可能性があります。Active Recordでスレッドよりパフォーマンスがよいものが使えるなら乗り換えたいところですが、悲しいことにそうはいきません。
config.active_support.isolation_level
に:fiber
を設定してみましょう。
module Myapp
class Application < Rails::Application
config.active_support.isolation_level = :fiber
end
end
このとき、ConnectionPool
がデータベースとどのようにやりとりするかを見てみましょう。
> Rails.application.config.active_support.isolation_level
=> :fiber
> ActiveRecord::Base.connection_pool.send(:current_thread)
=> #<Thread:0x00007f9ba485fa88 run>
Active RecordのConnectionPool
には、Active Supportの分離レベルが反映されていません。
変更後
ありがたいことに、ConnectionPool
のやりとり全体がスレッドまたはFiberのいずれかを使うようになりました(#44219)。
それでは早速見てみましょう。
> Rails.application.config.active_support.isolation_level
=> :fiber
> ActiveRecord::Base.connection_pool.send(:current_thread)
=> #<Fiber:0x00007fb5515d5238 (resumed)>
できました!しかしFiber
に変わるとどんな意味があるのでしょうか。近い将来、RailsのI/O操作のパフォーマンス向上が期待できます。現在のRailsではIsolatedExecutionState
で実行を抽象化していますが、少なくともこれについてはそうなるでしょう。しかしRailsのコードベースを見渡すと、スレッド中心の実装にハードコードされているコードがまだたくさんあるようです。Railsコアチームの仕事はまだ山積みです。今日にでもコントリビューションしましょう!
概要
元サイトの許諾を得て翻訳・公開いたします。
参考: 週刊Railsウォッチ20220228 Active Recordの
ConnectionPool
をFiberセーフにした