
2026/01/14 18:25
Redis から SolidQueue への移行を予定しています。
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
## Summary Rails 8はデフォルトスタックからRedisを削除し、ジョブキューイング、キャッシュ、およびActionCableメッセージングをPostgreSQLベースのソリューションに置き換えます:SolidQueue、SolidCache、SolidCableです。 *SolidQueue* は `FOR UPDATE SKIP LOCKED` クエリを使用してロック競合を回避し、約 **200–300ジョブ/秒** を実現します。ジョブメタデータは 3 つのテーブルに格納されます ― `solid_queue_jobs`、`solid_queue_scheduled_executions`、および `solid_queue_ready_executions`。ワーカーは準備済み実行を **0.2 秒ごと** にポーリングし、スケジューラは期限切れジョブを ready テーブルへ移動させ、監督者はハートビートを監視します。繰り返しタスクは `config/recurring.yml` で自然言語のスケジュール(例:「毎日午前2時」など)で定義されます。並列制限(`limits_concurrency`)は `solid_queue_semaphores` と `solid_queue_blocked_executions` に保存されます。 移行には、キューアダプタを `:solid_queue` に変更し、`solid_queue` gem をインストールし、データベースマイグレーションを実行し、Sidekiq の cron エントリを `recurring.yml` に変換し、Procfile を更新して Redis/Sidekiq gems を削除する必要があります。Rails 8 はこれらのコンポーネントを自動設定します;別々の DB 接続は推奨されますがオプションです。 *ActionCable* の統合には `solid_cable` gem が使用され、独自のデータベース接続と **0.1 秒(≈100 ms)** のポーリング間隔でほぼリアルタイム通信を実現します。 SolidQueue は **秒あたり 100 ジョブ未満** または **>100 ms の遅延に寛容な** ワークロード向きです;より高いスループットやサブ1 ms 遅延シナリオでは Redis が依然として有効です。 無料でオープンソースの Mission Control Jobs ダッシュボードは `/jobs` にマウントされ、リアルタイムステータス、失敗検査、再試行/破棄制御、およびキューテーブルに対する SQL クエリ実行機能を提供し、追加インフラなしで観測性を向上させます。 総じて、Rails 8 はバックグラウンド処理を PostgreSQL に集中化し、外部依存関係を削減するとともに、必要に応じて監視とスケーリングのツールを提供します。
本文
Rails 8 は Redis を標準スタックから除外
ジョブ、キャッシュ、リアルタイムメッセージは、新しい Solid ファミリーで処理されます。
| 機能 | 旧ソリューション | 新ソリューション |
|---|---|---|
| ジョブキューイング | Sidekiq + Redis | SolidQueue(PostgreSQL) |
| キャッシュ | Rails Cache + Redis | SolidCache(PostgreSQL) |
| ActionCable | redis‑ベースの pub/sub | SolidCable(PostgreSQL) |
ほとんどの Rails アプリでは、Redis を廃止しても問題ありません。
Redis の真のコスト
月額ホスティング料金以外に、Redis には以下が追加されます。
- デプロイ・バージョン管理・パッチ適用・監視
- 永続化戦略(RDB スナップショット、AOF ログ、または両方)
- メモリ上限と排出ポリシー
- Rails と Redis の間のネットワーク接続とファイアウォールルール
- クライアント認証
- 高可用性クラスタオーケストレーション
- Sidekiq プロセスのライフサイクル管理
ジョブが失敗した場合、異なる意味論とクエリ言語を持つ 2 つ のデータストアをデバッグし、別々のバックアップ戦略を維持する必要があります。
「Redis なし」のスタックではすべてが一箇所に集約されます。Rails や PostgreSQL が停止すると、すべてのサービスも同時に停止するため運用が簡素化されます。
SolidQueue の仕組み
PostgreSQL の SKIP LOCKED
SKIP LOCKEDPostgreSQL 9.5 で追加された
FOR UPDATE SKIP LOCKED は次を実現します。
- 行を排他ロック(
)FOR UPDATE - 既にロックされている行はスキップ(
)SKIP LOCKED
これにより、データベースバックエンドのキューが大規模でも機能します。
SELECT * FROM solid_queue_ready_executions WHERE queue_name = 'default' ORDER BY priority DESC, job_id ASC LIMIT 1 FOR UPDATE SKIP LOCKED;
空きワーカーが次に利用可能なジョブを取得し、ロック競合やブロッキングは発生しません。処理後、ワーカーはロックを解放して実行レコードを削除します。
コアテーブル
| テーブル | 目的 |
|---|---|
| ジョブメタデータ(名前・クラス・タイムスタンプ)を永続的に保持。すべてのジョブが永久保存されます。 |
| スケジュールされたジョブをその時間になるまで保管します。 |
| 即時実行可能なジョブを格納し、ワーカーがここから取得します。 |
MVCC と自動 vacuum が高速挿入・削除を特別調整なしで処理します。
ワーカ種別
- Workers –
をポーリング(例:0.1 s)し、ready_executions
でジョブを取得。FOR UPDATE SKIP LOCKED - Dispatchers –
を毎秒チェックし、期限が来たジョブを ready テーブルへ移動。scheduled_executions - Schedulers – 定義されたタイムテーブルに従い再帰タスクを投入。
- Supervisor – ハートビートを監視し、クラッシュしたプロセスを再起動。
各プロセスは異なるテーブルで動作し、インターバルは負荷に合わせて最適化されます。すべての協調は標準 SQL トランザクションで実現します。
再帰ジョブのスケジューリング
SolidQueue の
config/recurring.yml は親しみやすい形式です。
# config/recurring.yml production: cleanup_old_sessions: class: CleanupSessionsJob schedule: every day at 2am queue: maintenance send_daily_digest: class: DailyDigestJob schedule: every day at 9am queue: mailers refresh_cache: class: CacheWarmupJob schedule: every hour queue: default
スケジューラが実行されると、期限の来たジョブをキューに投入し、次回の発生時刻を自動で設定します(例:毎時間タスクは翌時間に再度エンキュー)。
この決定的パターンはクラッシュ耐性が高く、GoodJob の設計と類似しています。
ジョブ同時実行制御
Sidekiq Enterprise は有料機能として同時実行制限を提供します。SolidQueue では 無料で 実装できます。
class ProcessUserOnboardingJob < ApplicationJob limits_concurrency to: 1, key: ->(user) { user.id }, duration: 15.minutes def perform(user) # 複雑なオンボーディングワークフロー end end
limits_concurrency は、ユーザーごとに同時実行を 1 件に限定します。duration により、ワーカーがクラッシュした場合でもセマフォは期限切れで解放され、デッドロックを防止します。
実装では次のテーブルを使用:
– 同時実行制限を追跡solid_queue_semaphores
– セマフォ待ちのジョブを保持solid_queue_blocked_executions
すべてがデータベースネイティブで、外部協調は不要です。
Mission Control で監視
Mission Control は無料・オープンソースで、Rails 8 の Solid エコシステムに最適化されています。
# config/routes.rb mount MissionControl::Jobs::Engine, at: "/jobs"
主な機能:
- すべてのキューにわたるリアルタイムジョブステータス
- 失敗したジョブの完全スタックトレース
- バッチ再試行/破棄コントロール
- スケジュールジョブのタイムライン可視化
- 再帰ジョブ管理
- キュー別メトリクスとスループットグラフ
SQL を使用しているため、ジョブデータを直接クエリできます。
SELECT j.queue_name, COUNT(*) AS failed_count FROM solid_queue_failed_executions fe JOIN solid_queue_jobs j ON j.id = fe.job_id WHERE fe.created_at > NOW() - INTERVAL '1 hour' GROUP BY j.queue_name;
移行パス:Sidekiq → SolidQueue
-
キューアダプタを変更
# config/environments/production.rb config.active_job.queue_adapter = :solid_queue -
Gem をインストール
bundle add solid_queue rails solid_queue:install rails db:migrate -
スケジュールを変換
旧(
)config/sidekiq.yml:schedule: cleanup_job: cron: '0 2 * * *' class: CleanupJob新(
)config/recurring.ymlproduction: cleanup_job: class: CleanupJob schedule: every day at 2am -
Procfile を更新
web: bundle exec puma -C config/puma.rb jobs: bundle exec rake solid_queue:start -
古い Gem を削除
# Gemfile – 削除 gem "redis" gem "sidekiq" gem "sidekiq-cron"その後実行:
bundle install bundle clean --force
既存の
ActiveJob クラスは変更せずに動作します。
SolidQueue を使わないケース
- 高スループット(秒間数千ジョブ)
- サブ 1 ms ラテンシー が必要なリアルタイム入札、HFT 等
- 複数サービス間の複雑な pub/sub
- Redis の原子操作が有利になる高頻度レートリミットやカウンタ
ジョブ処理が 100 jobs/s 未満、またはラテンシ許容値が 100 ms 超であれば、SolidQueue は十分に機能します。
実装手順ガイド
-
新しい Rails 8 アプリを作成
rails new myapp --database=postgresql cd myapp -
キュー用データベースを設定(任意)
# config/database.yml development: primary: &primary_development <<: *default database: myapp_development queue: <<: *primary_development database: myapp_queue_development migrations_paths: db/queue_migrateSolidQueue に接続させる設定:
# config/environments/development.rb Rails.application.configure do config.active_job.queue_adapter = :solid_queue config.solid_queue.connects_to = { database: { writing: :queue } } end -
データベースを準備
rails db:prepare -
Mission Control の認証設定
# config/environments/development.rb config.mission_control.jobs.http_basic_auth_user = "dev" config.mission_control.jobs.http_basic_auth_password = "dev" -
ルーティングに Mission Control をマウント
# config/routes.rb mount MissionControl::Jobs::Engine, at: "/jobs" -
Procfile.dev を作成
web: bin/rails server jobs: bundle exec rake solid_queue:start -
スタックを起動
bin/dev -
ジョブでテスト
rails generate job EmailReport
に追加:app/jobs/email_report_job.rbclass EmailReportJob < ApplicationJob queue_as :default retry_on StandardError, wait: :exponentially_longer, attempts: 5 def perform(user_id) user = User.find(user_id) ReportMailer.weekly_summary(user).deliver_now end end即時実行とスケジュール実行:
EmailReportJob.perform_later(User.first.id) EmailReportJob.set(wait: 1.week).perform_later(User.first.id)再帰化は
に追加:config/recurring.ymlproduction: weekly_reports: class: EmailReportJob schedule: every monday at 8am queue: mailers
を開いて Mission Control を確認。http://localhost:3000/jobs
よくある落とし穴
-
単一データベース構成 –
を通常のマイグレーションにコピーし、ファイルを削除してdb/queue_schema.rb
を取り除き、config.solid_queue.connects_to
。小規模アプリでは便利ですが、運用柔軟性は犠牲になります。rails db:migrate -
本番認証 – Basic Auth から OAuth や API キーなど強固な仕組みに置き換えます(例):
# config/initializers/mission_control.rb Rails.application.configure do config.mission_control.jobs.base_controller_class = "AdminController" end -
ポーリング間隔 – デフォルトはスケジュールジョブ = 1 s、ready ジョブ = 0.2 s。Sidekiq から移行した後にスループットが低下したらこれらを確認し、サブ秒ラテンシが必要なら調整します。
-
ActionCable / Turbo Streams –
に別のdatabase.yml
接続を追加し、cable
を設定。0.1 s のポーリングで約 100 ms ラテンシを実現でき、ライブ更新に十分です。config/cable.yml
スケールは可能か?
Nate Berkopec の公式式:
必要インスタンス数 = リクエストレート (req/sec) × 平均応答時間 (sec)
例:
- 100 req/min → 1.67 req/s
- 200 ms avg → 0.2 s
1.67 × 0.2 ≈ 0.33 → 約 0.3 インスタンスが必要。
大規模 Rails チーム(例:37signals)は PostgreSQL 単体で約 230 jobs/s を処理しており、SolidQueue が十分に高負荷を扱えることを示しています。
| 機能 | Redis + Sidekiq | SolidQueue |
|---|---|---|
| 設定の複雑さ | 別サービス・設定が必要 | アプリ内で完結 |
| クエリ言語 | Redis コマンド | SQL |
| 監視 | 別ダッシュボード | アプリと同じ環境 |
| 故障モード | 6+ 種類 | 2 種類 |
| ジョブスループット | 約 1000 s⁻¹ | 約 200–300 s⁻¹ |
| 適用範囲 | 99.9% のアプリ | 95% のアプリ |
結論
Redis と Sidekiq は強力ですが、多くの Rails アプリにとって不要な運用オーバーヘッドをもたらします。SolidQueue、SolidCache、SolidCable は PostgreSQL(または SQLite/MySQL)上で同等の機能を提供し、インフラ構成を簡素化しつつ堅牢なジョブキューイング・キャッシュ・リアルタイムメッセージングを実現します。
ぜひ SolidQueue を試してみてください。スタックをシンプルにし、運用負担を軽減し、より優れたプロダクト構築に集中できます。