Rails 8.2のActionCableアダプタ変更に対応したSolid Cableの互換性修正
Rails 8.2でActionCableのアダプタ内部構造が変更されたことに伴い、Solid Cableが新旧両方のインターフェースに対応できるよう修正されました。これにより、Rails 8.2以降と従来バージョンの両方で動作する互換性が確保されています。
背景
Rails 8.2でActionCableのアダプタ基底クラスが変更され、従来 @server 経由で参照していた mutex や event_loop などのリソースへのアクセス方法が変わりました。Solid Cableはこれまで @server.mutex と @server.event_loop に直接依存していたため、Rails 8.2上でそのまま動作させることができなくなっていました。
Rails 8.2以降では、アダプタ自身が mutex や executor を持つ構造へと移行しています。Solid Cableはこの変更に追従しながら、Rails 8.2未満の環境でも引き続き動作する必要がありました。
技術的な変更
@mutex と pubsub_executor の取得ロジックを抽象化し、Railsバージョンに応じて適切なリソースを選択する実装が導入されました。
initialize メソッドにおける @mutex の初期化が変更されました。Rails 8.2以降では @server が存在しない場合があるため、@server の有無を確認した上で @server.mutex を使用するか、新規に Mutex.new を作成するかを切り替えます。
変更前:
def initialize(*)
super
@listener = nil
end
変更後:
def initialize(*)
super
@mutex =
if defined?(@server)
@server.mutex
else
Mutex.new
end
@listener = nil
end
listener の初期化処理でも同様の変更が行われました。従来は @server.mutex と @server.event_loop を直接参照していましたが、それぞれ @mutex と pubsub_executor という抽象化されたアクセサ経由に変わっています。
変更前:
def listener
@listener || @server.mutex.synchronize do
@listener ||= Listener.new(@server.event_loop)
end
end
変更後:
def listener
@listener || @mutex.synchronize do
@listener ||= Listener.new(self, pubsub_executor)
end
end
def pubsub_executor
@pubsub_executor ||=
if respond_to?(:executor, true)
executor
else
@server.event_loop
end
end
Listener クラスのコンストラクタも変更されました。従来は event_loop のみを受け取っていましたが、アダプタ自身(self)も引数として受け取るようになり、delegate :logger, to: :@adapter によってロガーへのアクセスもアダプタ経由に統一されています。
変更前:
def initialize(event_loop)
super()
@event_loop = event_loop
end
変更後:
delegate :logger, to: :@adapter
def initialize(adapter, executor)
super()
@adapter = adapter
@executor = executor
end
Listener がアダプタ自身への参照を保持することで、logger をはじめとするアダプタのリソースへ @server を経由せずにアクセスできるようになっています。
設計判断
defined?(@server) と respond_to?(:executor, true) による動的な分岐 が採用されたことで、単一のコードベースで複数のRailsバージョンに対応する互換レイヤーが実現されています。
Rails 8.2未満では @server が存在し executor メソッドが存在しないため、従来通り @server.mutex と @server.event_loop が使用されます。Rails 8.2以降では @server が存在せずアダプタ自身が executor を持つため、新しいインターフェースが選択されます。この分岐は条件式の追加のみで実現されており、アダプタの本質的な動作ロジックには変更がありません。
respond_to?(:executor, true) の第二引数 true によって private メソッドも検索対象に含めている点も注目すべき設計です。新しいActionCableのインターフェースで executor がprivateメソッドとして定義されている場合にも対応できます。
まとめ
今回の修正は、@server 依存を抽象化することでRails 8.2のActionCable変更に対応したものです。defined? と respond_to? による条件分岐を最小限に留めつつ、Solid Cableを複数バージョンのRailsで継続して動作させる互換性を確保した実用的なアプローチといえます。