16 ポイント 投稿者 GN⁺ 2026-01-15 | 2件のコメント | WhatsAppで共有
  • Rails 8 は標準スタックから Redis 依存を排除し、SolidQueue・SolidCache・SolidCable を通じて、すべての処理を リレーショナルデータベース(RDB) 上で扱う構成へ移行
  • Redis は高速で安定している一方、設定・セキュリティ・クラスタ管理・バックアップなど運用の複雑さを招く
  • SolidQueue は PostgreSQL の FOR UPDATE SKIP LOCKED 機能を活用し、競合のない並列ジョブ処理を実現
  • 定期ジョブ、同時実行制御、監視ダッシュボード(Mission Control) など、Redis+Sidekiq の有料機能を 無料で提供
  • ほとんどの Rails アプリケーションでは SolidQueue で十分であり、超高速・リアルタイム処理が必要な一部のケースでのみ Redis を維持する必要がある

Redisの隠れたコスト

  • Redis には単純なホスティング費用以外にも、導入・保守・セキュリティ設定・HAクラスタ管理 など継続的な運用負担がある
    • Rails と Redis 間の ネットワーク接続とファイアウォール設定クライアント認証Sidekiq プロセスのオーケストレーション が必要
  • 障害発生時には Redis と RDBMS の2システムを同時にデバッグしなければならず、二重のバックアップ戦略も求められる
  • 一方、Redis を使わない Rails スタックでは PostgreSQL だけを管理すればよく、構成を単純化できる

SolidQueueの動作原理

  • PostgreSQL の FOR UPDATE SKIP LOCKED 機能を使い、ロック競合(lock contention) なしで複数のワーカーが同時にジョブを取得する
  • 主なテーブル構造
    1. solid_queue_jobs: ジョブのメタデータを保存
    2. solid_queue_scheduled_executions: 予約済みジョブを待機
    3. solid_queue_ready_executions: 実行準備ができたジョブのキュー
  • ワーカー・ディスパッチャ・スケジューラ・スーパーバイザ の各プロセスが異なるテーブルを定期的にポーリングしながら連携
  • PostgreSQL の MVCC 設計と autovacuum により、大量の挿入・削除処理も安定してさばける

繰り返しジョブのスケジューリング

  • SolidQueue は cron スタイルの繰り返しジョブ を標準で提供し、config/recurring.yml ファイルで設定する
  • スケジューラは実行時刻になったジョブをキューに入れ、次回実行時刻を自動で予約する
  • Fugit ライブラリで自然言語スケジュールを解析し、Concurrent::ScheduledTask でスレッドを生成
  • GoodJob の決定的(deterministic)スケジューリング方式を取り入れ、プロセス再起動後もスケジュールを維持する

同時実行制御機能

  • SolidQueue は POSIX セマフォパターン を使って、ジョブ単位ごとの同時実行制限をサポート
    • 例: limits_concurrency to: 1, key: ->(user) { user.id } を設定すると、ユーザーごとに1ジョブだけ実行される
  • セマフォの有効期限(duration)を指定し、ジョブ衝突やデッドロックを防止
  • 関連テーブル
    • solid_queue_semaphores: 同時実行制限を追跡
    • solid_queue_blocked_executions: 待機中のジョブを保存

Mission Controlによる監視

  • Mission Control Jobs は Rails 8 向けの無料オープンソースダッシュボードで、/jobs パスに簡単にマウントできる
  • 主な機能
    • リアルタイムのキュー状態、失敗ジョブの追跡、再試行/破棄の制御
    • 予約・繰り返しジョブのタイムライン可視化
    • キューごとの処理量とメトリクスのグラフ
  • SQL ベースのクエリに対応しており、追加ツールなしでデータベースから直接分析可能

SidekiqからSolidQueueへの移行

  • ステップ1: config.active_job.queue_adapter = :solid_queue を設定
  • ステップ2: bundle add solid_queue の後、rails solid_queue:installdb:migrate を実行
  • ステップ3: sidekiq.yml の cron スケジュールを recurring.yml に変換
  • ステップ4: Procfilejobs: bundle exec rake solid_queue:start を追加
  • ステップ5: Redis・Sidekiq 関連の gem を削除
  • 既存の ActiveJob コードは修正なしでそのまま動作する

Redisが依然として必要な場合

  • 毎秒数千件を超える継続的なジョブ処理
  • 1ms 未満のレイテンシ が必須のリアルタイムシステム
  • 複雑な pub/sub 構造高度なレート制限・カウンタ演算 が必要
  • 例として Shopify は毎秒 833 リクエスト、1,172 のワーカープロセスを運用し、Redis インフラを利用している

実際の実装ガイド

  • Rails 8 の新規アプリ作成時には SolidQueue・SolidCache・SolidCable が自動構成される
  • config/database.yml専用の queue データベース接続 を設定することが推奨される
  • Mission Control の認証を追加し、/jobs ルートをマウント
  • Procfile.devjobs: bundle exec rake solid_queue:start を追加し、bin/dev 実行で全体を起動
  • テストジョブを作成したあと、Mission Control で状態を確認できる

よくある問題と解決策

  • 単一データベース構成 も可能だが、運用の柔軟性は下がる
  • 本番環境の Mission Control には必ず認証の追加が必要
  • ポーリング間隔 のデフォルト値は予約ジョブ 1 秒、即時ジョブ 0.2 秒で、大半のアプリに適している
  • ActionCable/Turbo Streams を使う場合は、SolidCable を別DB接続として設定する必要がある

拡張性と性能

  • SolidQueue は大半の Rails アプリで十分にスケール可能
  • PostgreSQL ベースで 毎秒 200〜300 ジョブ処理 が可能で、37signals は Redis なしで1日2,000万ジョブ を処理している
  • 比較表
    項目 Redis + Sidekiq SolidQueue
    設定の複雑さ 別サービスが必要 内蔵DBを利用
    クエリ言語 Redis コマンド SQL
    監視 別ダッシュボード Mission Control
    障害シナリオ 6個以上 2個
    処理量 数千件/秒 200〜300件/秒
    適した対象 99.9% のアプリ 95% のアプリ

結論

  • Redis と Sidekiq は優れた技術だが、大半の Rails アプリケーションには過剰な複雑さとコストをもたらす
  • SolidQueue は単一データベースを基盤に、運用の単純化・コスト削減・保守効率の向上を実現する
  • Rails 8 時代の標準的な選択肢として、SolidQueue への移行が推奨される

2件のコメント

 
kaydash 2026-01-17

Redisはいいけど。

 
GN⁺ 2026-01-15
Hacker Newsの意見
  • すべてのオープンソース作者には、自分のプロジェクトの範囲をコントロールする権利があると思う
    ただ、うちのチームがgood_jobからSolidQueueに切り替えたのは後悔している
    BasecampはMySQL中心なので、RDBMSエンジン特化クエリを受け入れていない。GitHubのIssueを見ると、MySQL性能にばかり注力しているのがわかる
    それに、まだバッチジョブ対応もない(関連PR

    • 最悪の組み合わせに聞こえる。うちもMySQLを使っているが、エンジン特化クエリなしではやっていけない
      複雑なJOINでMySQLがクエリプランを誤ることが多いので、私はSTRAIGHT_JOINで順序を強制している。将来への備えでもある
    • そこまでMySQLに深く結びついているなら、MySQL専用機能を使うほうが理にかなっていないか? 何か見落としている気がする
    • GoodJobをSolidQueueより勧める理由を、もう少し具体的に聞きたい
      私はresqueから移行する候補としてこの2つを比較中だ。GoodJobはpg専用機能のため、pgbouncerのtransactionモードと互換性がない
      セッション維持が必要で面倒だが、性能向上はたいていの規模では大きな意味がない
      それでも、GoodJobの開発モデルとコードの可読性にはずっと信頼感がある
    • 同意する。good_jobはPostgresベースのキューとして理想的なアプローチだ
    • SolidQueueはまだよくわからないが、good_jobは使ってみて本当に快適だった。ちゃんと動く
  • 本番環境を単純化できるなら、いつでも良いことだ
    Railsで理想的なのは、Redisへ簡単に切り替えられる構成だと思う
    SolidQueueで始めて、スケーラビリティの限界にぶつかったらRedisへ移行できると良い
    ほとんどのRailsアプリはトラフィックが大きくないので、2つのシステムを維持するほうがむしろ複雑だ

    • RailsはActive Jobという抽象化されたAPIを提供しているので、Redisへの切り替えはかなり簡単だ
      もちろん特定のキュー実装に依存するアプリもあるが、一般的には設定を変えるだけで済む
    • Redis AOFモードが、WALのようにすべての変更を記録するのか気になる
      ログが大きくなりすぎないようにスナップショットも併用するのか、そしてこれが分散モードでも動くのか知りたい
    • トランザクションに依存しすぎると、移行が難しくなる
      特にジョブ生成が他のDB変更と一緒に起こる場合、その保証を失うのが問題だ
    • Railsはバックグラウンドジョブプロセッサを本番DBと分離していないので混乱する
      Redisはこの点で、軽量で独立した状態ストアとして有利だった
      SolidQueueはこうした分離を明確にしていないように思える(riverqueue.com
  • 自分のサイドプロジェクトでSolidQueueを試してみた
    結論として、Sidekiqに問題がないならわざわざ変える理由はない
    Redisインフラをなくしたいときだけ検討する価値がある
    新規プロジェクトなら、GoodJobのほうがより成熟していてコミュニティも良い
    SolidQueueはUIがあまりに簡素で不便だった。インデックス最適化がされておらず、データが増えるとページが固まった
    RDBMSを使うと、コネクションプール管理コストが追加される点も考慮すべきだ

  • スケーラビリティを心配する人向けに言うと、ElixirのObanのベンチマークでは
    単一ノードで毎分100万件のジョブを処理している。ほとんどのアプリのジョブ量はそれよりずっと少ない

    • うちの会社でもObanを使っているが、ObanはnotifierとしてRedisを使うか、ポーリングを推奨している(scaling文書
    • ただ、そのベンチマークは実際のアプリとはかなり離れている
      5000件のジョブを一度にバッチ投入する構造なので、TPSは実際には200程度だ
      バッチなしで個別ジョブを投入すると、SQLトランザクション負荷はずっと大きくなる
  • 私たちはSolidQueue以前からDBにジョブを保存していた
    利点は、本番状態をそのまま開発環境へスナップショットできることだ
    ただしrate limiterはRedisに置いている。DB負荷を防ぐためだ

  • DBベースのキューの限界は大容量payload
    大きなJSONをキューに入れると、DB書き込みオーバーヘッドのせいで非効率になる
    Redis(Sidekiq)はこういう場合はるかに速い
    SolidQueue+SQLiteは、単にprimary keyを渡す用途なら悪くない
    ただし複数のワーカーが同じDBをポーリングすると、すぐにボトルネックになる

    • 実際にはジョブパラメータとしてIDを1つか2つだけ渡すのが一般的だ。それ以上は非効率だ
    • Redisもメモリ制約があるので、大きなpayloadには向いていない
      大きなデータはS3のような外部ストレージに置き、参照だけ渡すほうがよいと思う
    • どうせシステムの他の部分でもストレージは必要になるので、S3やgarageのような一時ストレージを使うのが現実的だ
      ベンチマーク結果をまとめた資料があるのか気になる
    • Sidekiqのドキュメントでも識別子だけを渡すよう勧めている
    • Redisに大きなpayloadを保存するのは悪い慣行だ。メモリが限られているからだ
  • SolidQueueではSKIP LOCKEDに触れているが、15分かかるジョブをトランザクションで維持するのは危険だ
    長時間開いたトランザクションはDB性能を損ない、ネットワーク切断にも弱い
    こうした構造はアンチパターンにつながりかねない。後で見たら、lease方式のようだ

  • Postgres for everythingという哲学に共感する
    シンプルにPostgreSQL一つへ統合するのがよいと思う

    • 私もPG一本化の考え方は好きだが、「ハンマーしか持っていないと何でも釘に見える」という言葉をよく聞く
      このたとえにどう反論すべきかわからない
    • 最近はNVMeストレージがあまりに速く、Redisの利点が小さくなったと感じる
      複雑さを増やしてまでRedisを使う理由があるのかと思う
  • 「1ms未満のレイテンシが重要なビジネス」って、RailsでHFTをやるという話か?

    • もちろん違う。その会社はSimpleThreadの事例を見ると、HFTとはほど遠い
    • 取引エンジンはRailsでは動かさないだろうが、取引監視UIならRailsで作ることもあるだろう
  • Postgresが世界を食う

    • 私も同意する。いつかpg_kernel拡張が出たら、Linuxを消してしまうかもしれない
    • ただ、数年後には「Postgres for everything」も「MongoDB for everything」と同じく行き過ぎた流行だったと気づくかもしれない
    • それでもMySQLは保守しやすく、性能も十分だ(私はPostgresユーザーだがそう感じる)
    • 今はPGQMとPG_CRONを使っているが、昔のMySQL + Redis + AWS構成よりずっとすっきりしている
    • 結局重要なのは機能セットだ。RDBMSが世界を食う