Rails 8から標準!Solid Queueの基本と仕組みをサクッと理解する
Rails 8からデフォルトのバックグラウンドジョブバックエンドとして採用されたSolid Queue。
これまでRailsで非同期処理(Active Job)を行うにはSidekiq + Redisの構成が定石でしたが、Solid Queueの登場によりRDB単体で高性能なジョブ管理が可能になりました。
現在の案件でSolid Queueを本格利用したので備忘録として記載します。
Solid Queueとは?
Solid Queueは、DBベースのバックグラウンドジョブバックエンドです。
PostgreSQL、MySQL、SQLiteなどのSQLデータベースをそのままジョブキューのストレージとして使用します。
これまでの課題と解決策
- これまで (Sidekiqなど): ジョブ管理のためにRedisなどのKVSを別途用意する必要があり、インフラ構成やデプロイフローが複雑になりがちでした。また、それに伴い別途費用もかかりました。
-
Solid Queue: アプリケーションと同じDBを使用するため、インフラ構成がシンプルになります。また、最近のSQLの機能(
FOR UPDATE SKIP LOCKED)を活用することで、DBベースでありながら高いパフォーマンスを実現しています。
Solid Queueを使う3つのメリット
1. インフラ構成の簡素化(Redis不要)
最大の特徴は、Redisサーバーを管理・運用する必要がないことです。
個人開発やスタートアップ、コストを抑えたいプロジェクトにおいて、DBサーバー1つで完結するのは大きなメリットです(PaasのRedisアドオン代を節約できます)。
2. トランザクションの整合性
DBを使用するため、アプリケーションのデータ保存とジョブのエンキューを同一トランザクション内で行えます。
「ユーザー登録(DB保存)は失敗したのに、ウェルカムメール送信ジョブ(キュー登録)だけ走ってしまった」といった矛盾を防ぐことができます。
3. FOR UPDATE SKIP LOCKEDによる高速化
「DBをキューに使うと遅いのでは?」という懸念があるかもしれませんが、Solid Queueは現代的なSQL機能である FOR UPDATE SKIP LOCKED を使用しています。
これにより、複数のワーカーが同時にDBにアクセスしても、ロック待ちで詰まることなく、空いているジョブを高速に取得できます。
▪️FOR UPDATE SKIP LOCKEDとは
FOR UPDATE SKIP LOCKED は、データベース(SQL)で、複数のプログラムが同時に同じデータを処理しようとした時に、「行列を作らずに、空いている仕事からどんどん片付けていく」ための非常に便利な仕組みです。
一言で言うと、「他の人が作業中のデータは、待たずにスルーして、次のデータを取ってくる」という命令です。
基本的なアーキテクチャ
Solid Queueは主に3つのコンポーネントで動いています。
| コンポーネント | 役割 |
|---|---|
| Supervisor | 全体の監督役。ワーカーやディスパッチャーのプロセスを起動・監視し、停止したら再起動させます。 |
| Dispatcher | 「スケジュールされたジョブ(未来の日時指定など)」をチェックし、実行可能な状態(Ready)にしてキューに入れます。 |
| Worker | 実際の働き手。キューからジョブを取り出し(ポーリング)、処理を実行します。 |
導入と設定方法
Rails 8では標準ですが、Rails 7.1以降であれば簡単に導入できます。
1. インストール
bundle add solid_queue
2. インストーラーの実行とマイグレーション
Gemを入れたら、専用のインストーラーを実行して設定ファイルとマイグレーションファイルを作成します。
bin/rails solid_queue:install
このコマンドを実行すると、config/solid_queue.yml(設定ファイル)が生成され、マイグレーションファイルが作成されます。その後、DBにテーブルを作成します。
bin/rails db:migrate
これにより、データベース内に solid_queue_jobs や solid_queue_processes などのテーブルが作成されます。これらがRedisの代わりとしてジョブ情報を保持します。
ジョブのライフサイクル(ステータス遷移)
Solid Queueでは、ジョブの状態(ステータス)が変わるごとに、内部でレコードが移動していきます。
以下は、ジョブが生成されてから完了するまでの流れです。
- Ready (実行待ち): ワーカーがすぐに拾える状態のジョブです。
- Claimed (実行中): ワーカーがジョブを取得するとこの状態になります。他のワーカーからは見えなくなります(排他制御)。
-
Scheduled (スケジュール済み): 未来の日時を指定されたジョブです。時間が来ると
Readyに移動します。 -
Failed (失敗): エラーが発生したジョブです。ここからリトライすると再び
Readyに戻ります。
処理が正常に完了すると、ジョブデータはキューから削除(または履歴テーブルへ移動)されます。
3. アダプターの設定
Railsに「Active JobのバックエンドとしてSolid Queueを使う」と伝えます。 config/application.rb、または環境ごとの設定ファイル(config/environments/production.rbなど)に以下を記述します。
config.active_job.queue_adapter = :solid_queue
4. ワーカーの設定(config/solid_queue.yml)
ワーカーの数やスレッド数、ポーリング間隔などは config/solid_queue.ymlで管理します。 デフォルトでも動きますが、サーバーのスペックや要件に合わせて調整します。
default: &default
dispatchers:
- polling_interval: 1 # スケジュール済みジョブのチェック間隔(秒)
workers:
- queues: "*" # すべてのキューを処理
threads: 5 # 1ワーカーあたりのスレッド数
polling_interval: 0.1 # ジョブ取得のポーリング間隔(秒)
development:
<<: *default
production:
<<: *default
特定のキュー(例: mailers)だけを優先的に処理したい場合や、スレッド数を増やしたい場合はここを編集します。
ジョブの実行方法
開発環境でSolid Queueを起動するには、以下のコマンドを使用します。
bundle exec solid_queue:start
bin/dev や foreman を使用している場合は、Procfile.dev に以下を追記しておくと、サーバー起動と同時にワーカーも立ち上がるので便利です。
web: bin/rails server
worker: bundle exec solid_queue:start
まとめ
Solid Queueは、Redisへの依存を排除し、RDB単体で堅牢なジョブ管理を実現する強力なツールです。
- インフラがシンプルになる
- トランザクション整合性が保たれる
- Rails純正で安心感がある
特に新規のRailsプロジェクトであれば、迷わず第一選択肢として採用して良いでしょう。既存のSidekiqからの移行も、構成がシンプルになる分、長期的には運用コストの削減につながるはずです。
