こんにちは!Webアプリケーションエンジニアのレミーです!
この記事はEnigmo Advent Calendar 2025の21日目の記事です。
Rails 8がリリースされてから、バックグラウンドジョブシステムである Solid Queue に興味を持ち、調べてみました。
バックグラウンドジョブは、Ruby on Railsアプリケーションに重要な部分です。メール送信、画像処理、データ同期、キャッシュ更新、CSVファイルのエクスポートなど、これらはすべてアプリケーションの高速化とスムーズな動作を維持するために非同期で実行すべきタスクです。
長年、Railsのバックグラウンドジョブにおいて「Sidekiq + Redis」はほぼ基準とされてきました。しかし、Rails 8からは、Railsは公式にSolid Queueを導入しました。これはRedisを必要とせず、補助的なサーバーも不要な、ネイティブなキューシステムです。
この記事では、Solid Queueとは何か、その仕組み、どうしてRails 8以上のプロジェクトでSolid Queueを使用すべきかについて解説します。また、Sidekiqとの比較も行います。
Solid Queueとは?
Solid Queueは、データベースをジョブキューとして使用するバックグラウンドジョブシステムであり、Rails Solid Suite(Solid Cache, Solid Queue, Solid Cableを含む)の一部として開発されました。
Redisを使用するSidekiqとは異なり、Solid Queueはジョブをデータベースのテーブルに保存し、ワーカープロセスがそのジョブを読み取って実行します。
つまり:
- Redisが不要
- Sidekiqのインストールが不要
- 補助サーバーのコストがかからない
- ActiveJobと深く統合されている
- インストールが非常に簡単
これは、Railsをシンプルにするために生まれました。特にスタートアップ、小規模〜中規模のプロジェクト、またはコストを抑える必要がある環境に最適です。
仕組みとデータベース構造
Solid Queueは単一のシンプルなテーブルだけではなく、ジョブのライフサイクルを管理し、安全性とパフォーマンスを確保するために複数のテーブルを使用します。
重要なテーブルは以下の通りです:
- solid_queue_jobs: ジョブのメタデータ(クラス名、引数、キュー名、優先度、遅延ジョブの場合は
scheduled_at、ジョブIDなど)を保存します。 - solid_queue_ready_executions: 「実行準備完了」となったジョブを含みます。つまり、エンキューされたジョブで、ワーカーが拾える状態のものです。
- solid_queue_scheduled_executions: スケジュールされたジョブを含みます。まだ実行タイミングには達していません。
- solid_queue_claimed_executions: ワーカーが実行のために確保)したジョブ情報を保存し、複数のワーカーが同じジョブを実行しないためです。
- solid_queue_blocked_executions: ブロックされており、すぐに実行できないジョブを含みます。
- solid_queue_failed_executions: 実行後にエラーになったジョブを保存し、監視やデバッグに役立ちます。
このように明確に複数のテーブルに設計されているため、Solid Queueは役割を明確に分離でき、ロジックがクリアになり、管理しやすくなります。
Solid Queueにおけるジョブのライフサイクル
Solid Queueの仕組みと、なぜ複数の異なるテーブルが必要なのかを理解するために、ジョブがエンキューされ、ワーカーに拾われ、実行され、削除されるまでの完全なライフサイクルを説明します。
1. ジョブが呼び出される時(エンキュー)
MyJob.perform_later(args) を呼び出すと、Solid Queueはデータベースに対して2つの書き込み操作を行います:
solid_queue_jobsテーブルへの書き込み:ジョブのメタデータ("queue_name", "class_name", "arguments", "priority", "active_job_id", "scheduled_at", "finished_at", "concurrency_key" など)を保存します。- すぐに実行するジョブの場合:
solid_queue_ready_executionsにデータを追加します。このテーブルには、ワーカーが処理可能な準備完了ジョブが含まれます。
2. ワーカーが実行するジョブを探す(ポーリング)
ワーカーは solid_queue_ready_executions テーブルを継続的に「ポーリング」して、新しいジョブを取得します。ワーカーは以下の2つの作業を行います:
- 確保: ワーカーが
solid_queue_ready_executionsからジョブを選択すると、solid_queue_claimed_executionsテーブルにレコードを書き込みます。このレコードにより、2つのワーカーが同じジョブを実行することができません。 - 実行: クレームした後、ワーカーはジョブクラスの
performメソッドを呼び出して実行します。
3. ジョブ完了時、レコードの削除
ジョブが正常に実行されると、ワーカーは関連するすべてのテーブル(solid_queue_jobs, solid_queue_ready_executions, solid_queue_claimed_executions)からジョブを削除します。
ジョブのライフサイクルの簡単なまとめ
| 段階 | 関連テーブル | 目的 |
|---|---|---|
| ジョブのエンキュー | solid_queue_jobs |
ジョブのメタデータを保存 |
| ジョブ準備完了 | solid_queue_ready_executions |
ワーカーが拾える状態 |
| ワーカーによる確保 | solid_queue_claimed_executions |
1つのジョブを1つのワーカーが実行することを保証 |
| 実行 | なし | ワーカーが perform関数を呼び出す |
| 完了 | 複数のテーブルから削除 | レコードのクリーンアップ |
安全性、ジョブの「失う」を防ぐ仕組み
重要な要件の一つは、エンキューされたジョブが少なくとも1回は実行され、失われないことです。Solid Queueは、ワーカーのクラッシュ、強制終了、プロセスの不具合などのケースを以下の形式で処理します:
- 各ワーカーは起動時に
solid_queue_processesにレコードを作成し、定期的にlast_heartbeat_atを更新します。 - ワーカーがジョブをクレームする際、
solid_queue_claimed_executionsにプロセスIDと共にレコードを書き込みます。 - デフォルトはスーパーバイザープロセスがバックグラウンドで実行され、
processesテーブルをチェックします。許容時間を超えてheartbeatがないプロセス(例:5分以上)が見つかった場合、それを「失敗したワーカー」と見なします。 - スーパーバイザーはそのプロセスを削除し、そのワーカーが保持していたジョブを
readyキューに再エンキューして、他のワーカーが拾えるようにします。
これにより、ワーカーがクラッシュしてもジョブは失われず、データの整合性が保証されます。
Solid Queue と Sidekiq の比較
Solid QueueとSidekiqはどちらもRailsで人気のある非同期処理のシステムですが、以下の表で違いを明確にします。
| 基準 | Solid Queue (Rails 8) | Sidekiq |
|---|---|---|
| ストレージバックエンド | データベース (PostgreSQL / MySQL / SQLite) | Redis (インメモリ、非常に高速) |
| Railsとの統合 | ネイティブ、Rails 8からの公式組み込み | コアじゃない、gem経由で使用 |
| パフォーマンス | 小〜中規模のワークロードに良好 | 非常に高い、大規模ワークロードに最適 |
| 遅延 | DB使用のため比較的高い | 低い (Redis インメモリ) |
| インストール | 簡単、補助サービス不要 | 複雑、RedisとSidekiqの設定が必要 |
| 運用コスト | ほぼゼロ (既存DBを使用) | Redisのコストがかかる (特に本番環境) |
| 信頼性 | 高い (SQLトランザクション + ジョブクレーム) | 非常に高いがRedisに依存 |
| リトライのロジック | あり、DBに保存 | あり、強力かつ柔軟 |
| ダッシュボード | 強力なUIはまだない | Web UIが充実、リアルタイム監視が可能 |
いつ Solid Queue を選ぶべきか?
シンプル、軽量、ネイティブ、コスト節約を望むならSolid Queueを選びましょう。
いつ Sidekiq を選ぶべきか?
高速、強力、大規模システムに適したものを望むならSidekiqを選びましょう。
結論
Solid Queueは、インフラを簡素化し、Rails 8の大きな進歩を示しています。バックグラウンドジョブをコアフレームワークに直接統合することで、中小規模のプロジェクトはRedisやSidekiqに依存する必要がなくなり、安定性、信頼性の高いジョブ処理能力を確保しながら、運用コストを大幅に削減できます。
明日の記事の担当はエンジニアの宮川さんです。お楽しみに。