2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【聴講メモ】RubyKaigi 2026「Thread-Coordinated Ractors」を Ractor よく知らない自分が聞いてきた。

2
Last updated at Posted at 2026-04-30

はじめに

正直に言うと、RubyKaigi に行く前まで、
自分の Ractor 理解は「Ruby 3.0 で入った並列化のやつ」くらいでした。
Thread と何が違うのかも、実務でどこまで使えるのかも、ほぼわかってない状態。

そんな自分が今回 RubyKaigi 2026 で聞いてきたのが、
"Thread-Coordinated Ractors: The Pattern That Delivers"(Maciej Mensfeld 氏)
というセッションです。
聞き終わったあとは「Ractor の話というより、フレームワーク作者が現実と戦った記録を見せてもらった」
という感じでした。

この記事は、その内容を自分の理解整理を兼ねてまとめた聴講メモです。
解説記事というよりは「初心者が聞いてわかった範囲」なので、
認識違いがあったらコメントで指摘してもらえると助かります。

特に印象的だったのは、

  • Ractor を「アプリ利用者」に意識させない設計
  • 速度だけでなく、安定運用・互換性まで含めた現実解
  • 「8並列なら8倍速」ではない前提での、堅実な最適化

ようするに、フレームワーク作者視点の実装論。

この記事では、セッション内容をベースにしつつ、Rails / Ruby の実務で「ここ使えそう」を拾っていきます。


0. まずは超入門: 「並列」と「並行」って何が違うの?

Ractor の話に入る前に、まずはここだけ押さえておきます。

  • 並行(Concurrency): 複数のタスクを「進行中」の状態で扱う(切り替えながらでもOK)
  • 並列(Parallelism): 複数のタスクを物理的に同時に実行する

Ruby の Thread は「並行」は得意ですが、GVL(Global VM Lock)の影響で CPU バウンドな処理を
「並列」に伸ばしにくい、というのが文脈です。
Ractor はここを補うための仕組みだと考えると理解しやすいです。

初心者向けにたとえると、

  • Thread: 1 つのコンロで鍋を順番に入れ替えながら調理
  • Ractor: 複数コンロで同時に別の鍋を調理

こんなイメージです(ただし Ractor は「鍋の共有ルール」がかなり厳しめです)。

1. そもそも Ractor は何を解決するのか

Ruby の並行・並列手段を、おおまかに整理すると次のようになります。

  • Thread: I/O 待ちには有効。ただし GVL の制約で CPU バウンド処理の並列化は苦手
  • Process(fork): 真の並列化はできるが、メモリコスト・IPC コストが重め
    (CoWで初期コストは抑えやすい)
  • Ractor: データ隔離を前提に、Ruby レベルで安全に CPU 並列化を狙える
    (現状 experimental な側面あり)

ここで重要なのは、Ractor が「速い API」なのではなく、
共有メモリを禁止して安全性を担保する設計モデル、というのが本質です。

Ractor の本質は「制約による安全」

Ractor では、別 Ractor のオブジェクトを自由に書き換えられません。
その代わり、次のルールで通信します。

  • コピーして渡す(deep copy)
  • Ractor#send(obj, move: true) でムーブして渡す(送信元では利用不可になる)
  • shareable object を共有する(frozen かつ参照先も shareable である必要。例: Symbol/数値/true・false・nil/frozen String など)

ちなみに「shareable object」という言葉、自分はこのセッションで初めて聞きました。
その場では「frozen と似たようなもの?」くらいの理解で流してしまったので、
あとで公式ドキュメントを読み直して、もう少しちゃんと理解したいところです。

ただ、「Ractor 同士は勝手にメモリを触れない」
という大枠の方針は聞いていてわかりやすくて、
なるほどこれなら事故が減りそう、とは思いました。


2. なぜ Karafka のワークロードは Ractor にハマったのか

発表者の Maciej Mensfeld 氏は、Kafka 向け Ruby フレームワーク Karafka の作者です。

Karafka のユースケースは、だいたい次のような感じです。

  • 1 バッチで数千〜数万メッセージを扱う
  • デシリアライズ(JSON 等)が支配的なコストになる
  • I/O より CPU が律速になりやすい

これは Ractor の適性ときれいに一致します。

実務だと、どこを見て判断する?

Ractor を使うか迷ったら、まずは次の 3 つを見るのがおすすめです。

  1. CPU time の割合が高いか(例: JSON / MessagePack パース、圧縮、暗号化)
  2. 処理単位が独立しているか(副作用が少なく、順序依存が弱い)
  3. データを immutable 化しやすいか(freeze しやすい構造か)

この 3 つが揃わない場合は、Thread + I/O 並行や、
別プロセス分離のほうが素直なことが多いです。


3. セッションの核心: Thread-Coordinated Ractors パターン

ここが個人的に一番「へえ」となったところです。

自分は最初、「Ractor の話なんだから Ractor だけで全部やるんでしょ?」
と思いながら聞いていました。
ところが Mensfeld 氏の実装は、Coordinator Thread(ふつうのスレッド)+ Worker Ractors
というハイブリッド構成。
Thread と Ractor を組み合わせるのが現実解だったらしくて、ここで認識が一段変わりました。

構成

  • コーディネータースレッド(仕事配布・回収)
  • 複数の永続 Worker Ractor(実際の CPU 処理)
  • Queue ベースのシグナリング

この構成の利点は、次の通り。

  • 調停ロジックを 1 箇所に寄せられる
  • Ractor 間同期を最小化できる
  • Worker は純粋な「計算ノード」に集中できる

擬似コードで見る構造

「Coordinator Thread + Worker Ractors」は、次のイメージです。
自分の理解整理のために書いてみたので、
おかしいところがあったらコメントで教えてください。

※ 以下は Ruby 3.x 系の従来 Ractor API(send / take / Ractor.yield)ベースです。
Ruby 4.0 以降で導入が進んでいる Ractor::Port ベースの新 API では書き方が変わるはずなので、
そっちで動かしたい人は要注意です。

# Worker Ractor を 4 つ起動して使い回す(Persistent Ractors)
workers = Array.new(4) do
  Ractor.new do
    loop do
      payload = Ractor.receive
      break if payload == :__stop__
      Ractor.yield process(payload)
    end
  end
end

# Coordinator 側: round-robin で配る
batch.each_with_index do |item, i|
  # shareable 化してコピーコストと競合リスクを下げる
  workers[i % workers.size].send(Ractor.make_shareable(item))
end

# 配った件数ぶん、同じ規則で take する
results = batch.each_with_index.map do |_, i|
  workers[i % workers.size].take
end

# 後片付け(Worker をプールから落とす)
workers.each { |w| w.send(:__stop__) }

ポイントは 3 つです。

  • Coordinator は配る/集めるだけWorker は計算だけに役割を寄せる
  • Worker は使い回す(都度 Ractor.new しない)
  • 入力は Ractor.make_shareable で shareable に揃え、コピーコストを抑える

なお、上のコードは構造を見せるための簡略版です。実運用では、

  • 配布と回収を非同期に重ねる
  • Worker 側の例外を Coordinator 側で拾ってフォールバックする
  • 停止シグナル(:__stop__)の到達確認を行う

といった面倒ごとが入ってきます。このへんは実装で地味に効いてくるポイントです。

実装で効いた設計原則(体感順)

1) Frozen data in, frozen data out

入出力を凍結データ中心に揃えると、コピーコストと競合リスクを同時に減らせます。

2) Persistent Ractors

Ractor の都度生成・破棄は高コスト。
プールして使い回すのが前提です。

3) Zero blocking

Coordinator は待機中心、Worker は計算中心に役割分離。
「どこで詰まるか」が追いやすくなります。

4) Zero copy(可能な範囲で)

完全ゼロコピーは理想ですが、実務では「どこを凍結共有に寄せるか」の設計が勝負です。


4. 「利用者に何もさせない」設計が強い理由

このセッションがフレームワーク開発者に刺さるのはここです。

ユーザーコード全体を Ractor-safe にしてもらうのは現実的ではない。
だから内部ビルディングブロックを Ractor-safe にする。

Rails 文脈に置き換えると、

  • アプリ開発者に「全部 immutable で書いて」と要求しない
  • gem / framework 側で並列化可能な境界を切る
  • 既存 API を壊さず速度改善を届ける

という思想です。

これは単なる最適化テクニックというより、
「プロダクトとして採用されるための設計戦略」なんだろうな、と聞いていて思いました。


5. 性能結果の読み解き方(過度な期待をしない)

※ここでの数字は、Karafka のデシリアライズ系ワークロードでの計測値です。

  • 8 Ractor で典型 2〜2.1 倍
  • ベストケース 3.5 倍
  • メモリオーバーヘッドは小さい

「8 並列で 2 倍」が速いのか遅いのか、正直自分には判断がつきませんでした。
発表者が「このくらい出れば十分」みたいな温度感で話していたので、
そういうものなんだと思います。
後で考えてみると、コピーや調停のコストもあるし、
互換性の問題で一部を逐次処理に逃がす場面もあるはずなので、
リニアに伸びないのは当たり前なのかもしれません。

それでも「2倍」はかなり大きい

実運用で 2 倍は、

  • コンシューマ台数を減らせる
  • ピーク時の遅延を吸収しやすい
  • 同一コストで余裕を作れる

というインフラ・運用上の改善に直結します。

アプリ視点では地味でも、SRE / FinOps 視点ではインパクトが大きいです。


6. 実務で一番ハマりやすいのは「ライブラリ互換性」

Ractor 導入で最初にぶつかる壁は、アルゴリズムではなく互換性です。

このセクション、聞いていて「あ、そんなに簡単じゃないんだ」となりました。
Ractor を有効にすればコードがそのまま速くなるのかと思っていたら、
動かない gem や相性が悪い gem が意外とあるらしいです。

セッションでは CSV / YAML / Airbrake / MessagePack / dry-validation
などが具体例として挙がっていました。
自分は半分くらい名前を知らない gem だったので、そっちはあとで調べてみるつもりです。

※ gem の対応状況は変わるので、実際に使う前に最新版での挙動確認がおすすめです。

対策の現実解

  • Ractor 境界を狭く保つ(非対応 gem を境界外に置く)
  • 前処理・後処理をメイン側で吸収(エラー整形、通知など)
  • フォールバック経路を必ず持つ(Ractor 失敗時に逐次処理へ)

全部を Ractor 化するより、効く部分だけを安全に並列化するほうが現実的です。


7. Rails アプリで試すなら、どこから始める?

個人的には、次の順序で進めるとやりやすそうです。

  1. 重い CPU 区間を可視化する
    • stackprof / rbspy / benchmark でボトルネック特定
  2. pure function 的な処理を切り出す
    • 入力と出力が明確な変換処理に寄せる
  3. immutable 化を先に進める
    • freeze しやすい DTO 構造に変える
  4. 小さな Ractor プールで A/B
    • まず 2〜4 worker で効果測定
  5. 互換性と障害時フォールバックを先に実装
    • 速さより先に可用性を担保

特に 5 は重要で、「速いけど落ちる」は本番では価値がありません。

ここは自分も「まず速くしたい」に寄りがちです。
今回のセッションを聞いて、速くする前に「失敗したときに戻せるか」を決めておく
という順序が大事だなと思いました。
次に Ractor を触る機会があったら、まずここから設計したいです。


8. このセッションから得られる学び

聞く前は「Ractor すごい!」みたいな話を期待していたんですが、
実際には Ruby VM の制約と向き合いながら、API 利用者体験を壊さず、
実測ベースで改善していく「地味で実務寄り」のセッションでした。
そして、それがすごく良かったです。

Ractor は万能ではない。でも条件が揃う領域なら、Ruby でもまだまだ攻められる。
それを実例で見せてもらえたのが、自分にとっての一番の収穫です。


参考文献

この記事は下記の記事と聴講を参考に書かせていただきました。

2
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?