$30 off During Our Annual Pro Sale. View Details »
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ActiveJobUpdates
Search
Kuniaki IGARASHI
December 18, 2025
Technology
1
74
ActiveJobUpdates
Active Job 近況 - Continuations, Solid Queue
Kuniaki Igarashi / igaiga
RailsTokyo #2
Kuniaki IGARASHI
December 18, 2025
Tweet
Share
More Decks by Kuniaki IGARASHI
See All by Kuniaki IGARASHI
roppongirb_20250911
igaiga
1
550
igaiga Ruby Association Activity Report 2025 LT
igaiga
1
110
KaigiOnRails2024
igaiga
13
21k
RuboSensei
igaiga
0
310
Shibuya.rb-2023-04-27-igaiga
igaiga
1
550
Ginza Rails27 igaiga
igaiga
9
14k
tork09igaiga
igaiga
2
370
Road to white mages
igaiga
1
730
Road to white mages
igaiga
8
4.1k
Other Decks in Technology
See All in Technology
エンジニアリングマネージャー はじめての目標設定と評価
halkt
0
290
5分で知るMicrosoft Ignite
taiponrock
PRO
0
380
新 Security HubがついにGA!仕組みや料金を深堀り #AWSreInvent #regrowth / AWS Security Hub Advanced GA
masahirokawahara
1
2.1k
エンジニアリングをやめたくないので問い続ける
estie
2
1.2k
今からでも間に合う!速習Devin入門とその活用方法
ismk
1
720
Microsoft Agent 365 についてゆっくりじっくり理解する!
skmkzyk
0
350
re:Invent 2025 ~何をする者であり、どこへいくのか~
tetutetu214
0
220
Kubernetes Multi-tenancy: Principles and Practices for Large Scale Internal Platforms
hhiroshell
0
120
Kiro Autonomous AgentとKiro Powers の紹介 / kiro-autonomous-agent-and-powers
tomoki10
0
510
regrowth_tokyo_2025_securityagent
hiashisan
0
250
AI-DLCを現場にインストールしてみた:プロトタイプ開発で分かったこと・やめたこと
recruitengineers
PRO
2
150
AWS re:Invent 2025で見たGrafana最新機能の紹介
hamadakoji
0
390
Featured
See All Featured
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
132
19k
Rebuilding a faster, lazier Slack
samanthasiow
84
9.3k
Visualization
eitanlees
150
16k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
10
730
Designing for humans not robots
tammielis
254
26k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
34k
Producing Creativity
orderedlist
PRO
348
40k
Done Done
chrislema
186
16k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
26
3.2k
Why You Should Never Use an ORM
jnunemaker
PRO
61
9.6k
Scaling GitHub
holman
464
140k
Mobile First: as difficult as doing things right
swwweet
225
10k
Transcript
Active Job 近況 - Continuations, Solid Queue ガーネットテック373株式会社 五十嵐邦明 /
igaiga 2025/12/18 RailsTokyo #2
自己紹介 五十嵐邦明(igaiga) ガーネットテック373株式会社 代表取締役 フリーランスのRailsエンジニア プログラミングスクール「フィヨルドブートキャンプ」顧問 X: igaiga555 銀座Rails#27 Railsの学び方あるいは本の書き方そして教え方
著書 パーフェクトRuby on Rails[増補改訂版] Railsの練習帳 ゼロからわかる Ruby超入門 Railsの教科書 RubyとRailsの学習ガイド ほか
今日の内容 近頃 Active Job に追加された機能について話します Continuations(Rails8.1): ジョブを中断再開できる機能 Solid Queue(Rails8.0): DBをキューとして使うActive
Job用バックエンド その他の変更点の話は時間が足りなくなってしまったので今日は話しません
なぜ今日この話をするのか(下心) とある本の改訂作業をしています 量が膨大なのでいつ出るのか遠い目になります 本が出るまで公開しないのはもったいない RailsTokyoで話して、本も出す二毛作作戦 内容についてフィードバックをもらえると本も良くなるので嬉しいです 時間はたっっっぷりあるので、後で使ってみたフィードバックも大歓迎です
Active Job Continuations Rails8.1新機能 ジョブを中断させ進捗情報と共にキューへ保存、再開時に続きから実行する機能 進捗を記録する機能 ステップ カーソル ステップの中でより詳細に進捗を記録する機能 ジョブを複数のステップで定義する実装が必要
ジョブをキューへ保存できないときは、再開できない たとえばOOM Killerに非同期バックエンドプロセスがkillされたときなど
Active Job Continuations 今日の説明の流れ ステップ機能をつかった進捗記録 初回時と再開時のコード実行順 ステップの中で細かく進捗記録するカーソル機能 中断の判断をする地点「チェックポイント」 中断と再開についての詳細
ステップとは Continuationsで進捗を記録する機能 Continuationsを利用するにはステップで区切る実装が必要 ジョブ中断時に完了したステップをジョブ中に記録してキューへ保存 再開時は前回完了したステップは実行しないでスキップする
ステップ定義コード例 class SomethingJob < ApplicationJob include ActiveJob::Continuable # Continuations機能を使う def
perform # ステップをブロックで定義 step :first do |step| # ブロック引数stepへActiveJob::Continuation::Stepオブジェクトが渡ってくる end # ステップをメソッドで定義 step :second private def second(step) # 引数stepへActiveJob::Continuation::Stepオブジェクトが渡ってくる end end
ステップらがどのように実行されるかの確認コード(中断なし) class SomethingJob < ApplicationJob include ActiveJob::Continuable def perform puts
"=== Start/Resume Job" # ステップに属さないコード step :first do |step| # ステップ puts "=== 1st step" end puts "=== Middle point" # ステップに属さないコード step :second # ステップ puts "=== Finish Job" # ステップに属さないコード end private def second(step) puts "=== 2nd step" end end
実行結果(中断なし、初回) === Start/Resume Job # ステップに属さないコード(初回も再開時も常に実行される) === 1st step #
ステップ === Middle point # ステップに属さないコード === 2nd step # ステップ === Finish Job # ステップに属さないコード ステップのコードおよびステップ外のコードが書いた順に実行される ステップに属さないコードは初回も再開時も常に実行される ステップのコードは前回実行されていたらスキップされる
ジョブを中断させるサンプルコード secondメソッド(2番目のステップ)を書き換えて初回実行時にジョブを中断させる step.resumed? : 再開実行されているかどうかを判断 ActiveJob::Continuation::Interrupt : ジョブを中断させる例外 def second(step)
# step: ActiveJob::Continuation::Stepオブジェクト # step.resumed?: 再開実行かどうか unless step.resumed? # 初回だけ raise ActiveJob::Continuation::Interrupt.new # 中断させる end puts "=== 2nd step" end
ステップらがどのように実行されるかの確認コード(中断あり) class SomethingJob < ApplicationJob include ActiveJob::Continuable def perform puts
"=== Start/Resume Job" # ステップに属さないコード step :first do |step| # ステップ puts "=== 1st step" end puts "=== Middle point" # ステップに属さないコード step :second # ステップ (初回だけ中断させる) puts "=== Finish Job" # ステップに属さないコード end private def second(step) # step: ActiveJob::Continuation::Stepオブジェクト unless step.resumed? # 初回だけ raise ActiveJob::Continuation::Interrupt.new # 中断させる end puts "=== 2nd step" end end
実行結果(2ndステップで中断、その後再開) # 初回 === Start/Resume Job # ステップに属さないコード(毎回実行) === 1st
step # ステップ === Middle point # ステップに属さないコード # ここで実行される2nd stepで中断 # 5秒待ってからジョブが再開する # 2回目 === Start/Resume Job # ステップに属さないコード(毎回実行) === Middle point # 1st stepは実行済みなのでスキップされている === 2nd step # ステップ === Finish Job # ステップに属さないコード
カーソル機能 ステップの中でさらに詳細に進捗を記録するカーソル機能 Step#cursor: 現在のカーソル値を取得 Step#set!: カーソルに値を代入 Step#advance!: カーソル値にsuccメソッドを呼び出してカーソル値を進める カーソルへ代入するオブジェクトはsuccメソッドを持つものにする step
:first do |step| p step.cursor #=> nil # 初期値はnil step.set! (step.cursor || 0) + 1 p step.cursor #=> 1 step.advance! p step.cursor #=> 2 (略)
カーソルの初期値 初回実行時のカーソル初期値はnil step :first do |step| p step.cursor #=> nil
# 初期値はnil ステップ定義に start: 引数を書くと、初回の初期値を設定できる step :first, start: 1 do |step| p step.cursor #=> 1 # 初期値はステップ定義のstartオプションでの指定値 再開時のカーソル値は、前回中断した時の最後の値
初回時と再開時のカーソル値 step :first do |step| p "=== step.cursor #=> #{step.cursor}"
step.set! (step.cursor || 0) + 1 # カーソル値に1を足す p "=== step.cursor #=> #{step.cursor}" raise ActiveJob::Continuation::Interrupt.new unless step.resumed? end # 初回 "=== step.cursor #=> " # 初回の初期値はnil "=== step.cursor #=> 1" # 再開時 "=== step.cursor #=> 1" # 再開時は前回終了時の値 "=== step.cursor #=> 2"
カーソル機能を使うコード例(配列の各要素を処理) step :first, start: 0 do |step| # カーソル初期値を0にする items[step.cursor..].each
do |item| # で説明 item.do_something # 今回のitemを処理 step.advance! # カーソルをsuccして進める end end items[step.cursor..] 初回 step.cursor: 0 items配列の先頭から処理開始 再開時 step.cursor: 前回終了時の値 items配列の未作業の要素から処理を再開
ジョブクラスにContinuationsの設定を書く class SomethingJob < ApplicationJob self.resume_options = { wait: 1.seconds,
queue: :resumed } self.max_resumptions = 3 wait(再開までの時間): デフォルトは5.seconds queue(中断時に入れるキュー): デフォルトは初回と同じ max_resumptions(最大再開回数): デフォルトはnil、無制限に再開可能 注意: ActiveJobには以前からContinuations機能とは別のリトライ機能がある リトライ設定 retry_on StandardError, attempts: 3 と組み合わせ注意 retry_jobメソッド: Continuations機能の中断再開 retry_onメソッド: 従来のリトライ 設定値詳細: https://api.rubyonrails.org/classes/ActiveJob/Continuation.html
中断を判断するチェックポイント 前述のコードはActiveJob::Continuation::Interrupt例外を明示的に投げた 実際はジョブが判断してこの例外を投げる 判断(後述)はチェックポイントで行われ、次の地点に置かれる 各ステップ開始前(最初のステップを除く) set! メソッド (カーソル値を設定) advance! メソッド
(カーソル値を進める) checkpoint! メソッド (明示的にチェックポイントを置く)
チェックポイントでの判断方法 ジョブが queue_adapter.stopping? メソッドを呼び出して中断するか判断 結果trueのとき ActiveJob::Continuation::Interrupt 例外を発生させ中断作業 queue_adapter.stopping? がtrueになった瞬間にジョブが中断するのではない 次の状況までジョブは実行を続ける
チェックポイントで結果判断するまで プロセスが停止するまで
中断作業の内容 ジョブをシリアライズしてキューに再投入する 進捗情報はシリアライズされたジョブ内のcontinuationキーに記録される 進捗情報に記録される内容 完了したステップのリスト 進行中のステップがある場合はそのステップとカーソルの情報 キュー中のジョブ情報は例えばMission Control Jobsで見れる Mission
Control Jobs: Solid Queueで使えるキュー状況閲覧ツール
ジョブのContinuationsに関する記録(Mission Control Jobsで閲覧) completed: 完了ステップ current: 最後に実行していたステップ、カーソル値 resumptions: 再開実行した回数
ジョブが例外などを処理してエラー終了したときの特別対応 通常、ジョブが例外で終了したときは(従来の)リトライ機能でキューに戻る 進捗情報があるのに記録されず失われるのは不便なので防御策が採られている 次のケースでは再開させるジョブとして進捗情報を記録してキューに保存 ステップを完了している カーソルを進めている リトライではなく、Continuationsの再開としてキューに戻るので注意 Continuations機能を使うときは、リトライ(retry_on)は使わないのが良さそう Continuations機能だけで統一させて書く
isolatedオプションによる明示的なキュー再投入 「ジョブをキューへ保存できない異常終了時は、再開できない」を緩和する機能 isolatedオプション: ステップ開始前に必ずジョブ(進捗情報付き)をキューに保存 ステップ定義で step :some_step, isolated: true とisolatedオプション指定
チェックポイント間隔を短く設定できない、時間がかかるステップのときに有用 そのステップの開始前に進捗情報を記録したジョブをキューに保存する ジョブがキューに保存され、再開してからそのステップを実行する
Railsの練習帳 今日の内容はRailsの練習帳へ書きました Railsの練習帳 Active Job Continuations https://zenn.dev/igaiga/books/rails-practice-note/viewer/active_job_continuations Active Job Continuations
参考資料 Railsガイド Active Jobの基礎 https://railsguides.jp/active_job_basics.html RailsAPI Active Job Continuation https://api.rubyonrails.org/classes/ActiveJob/Continuation.html RailsAPI Active Job Continuation Step https://api.rubyonrails.org/classes/ActiveJob/Continuation/Step.html
Solid Queue Rails8.0新機能 DBをキューとして使うActive Job用バックエンド DBとしてMySQL、PostgreSQL、SQLiteなどが利用可能 FOR UPDATE SKIP LOCKED
句を使える場合はパフォーマンスが上げる ジョブをポーリングする時のブロックやロック待ちを回避 使えない場合もパフォーマンスは落ちるがSolid Queueは使える Rails8.0以降のproduction環境ではデフォルトでSolid Queueが使われる rails new した直後から使えるのが便利
Solid Queue 今日の説明の流れ Solid Queue 設定概要 Solid Queue を使ってみる Mission
Control Jobs ジョブの定期実行 Solid Queue へ移行すべきか?
Solid Queue 設定の概要 development環境でSolid Queueを使う設定 全部を説明すると長くなるので抜粋して説明します おおまかな流れ config/database.yml: queue用の設定を追加 マイグレーション実行:
bin/rails db:migrate:queue Solid Queue設定ファイル: config/queue.yml 設定全体はRailsの練習帳に書いたのでこちらを読んでください https://zenn.dev/igaiga/books/rails-practice-note/viewer/solid_queue
config/database.ymlへqueue用の設定を追加 development: primary: <<: *default database: storage/development.sqlite3 queue: <<: *default
database: storage/development_queue.sqlite3 migrations_paths: db/queue_migrate SolidQueue用の queue キーを追加 通常DBとは別のDBを指定できる キー名 queue は設定に書いた名前 :queue と同じにする config.solid_queue.connects_to = { database: { writing: :queue } } マイグレーション実行: bin/rails db:migrate:queue スキーマファイル: db/queue_schema.rb
ジョブクラスごとにSolid Queueを使う設定にもできる アプリ全体でSolid Queueを使う設定 config.active_job.queue_adapter = :solid_queue ActiveJobはジョブクラスごとにqueue_adapterメソッドでバックエンド切替可能 段階的移行時に便利 ジョブクラスごとにバックエンドをSolid
Queueにするコード class SomethingJob < ApplicationJob self.queue_adapter = :solid_queue
Solid Queueを使ってみる rails consoleを起動してジョブをキューへ追加 この状態ではキューへ追加はされるが、まだジョブ実行はされない irb> AsyncLogJob.perform_later(message: "from Solid Queue")
略 Enqueued AsyncLogJob (Job ID: 略) to SolidQueue(default) with arguments: {:message=>"from Solid Queue"} 略 bin/jobs start コマンドでバックエンド処理プロセスを起動してジョブ実行 デフォルトではログは log/development.log へ出力 bin/jobs コンソールには何も表示されない config/environments/development.rbへlogger設定を追加すると表示可能 config.solid_queue.logger = ActiveSupport::Logger.new(STDOUT)
Mission Control Jobs mission_control-jobs Gem https://github.com/rails/mission_control-jobs Active Jobアダプター向けのキューの状態を確認するWeb UI 現在はSolid
QueueとResqueがサポートされている ジョブキューやキュー内で待機中のジョブを確認する機能 失敗したジョブの情報閲覧、再試行、破棄する機能 適切なアクセス制限を追加する必要がある デフォルトではBASIC認証がかかっている 参考: TechRachoさん README翻訳記事 https://techracho.bpsinc.jp/hachi8833/2025_10_27/145131
Mission Control Jobs 設定の流れ Gemfileへ gem "mission_control-jobs" を追加 config/routes.rb へ追加
mount MissionControl::Jobs::Engine, at: "/jobs" デフォルトでBASIC認証がかかる BASIC認証用のuser, password情報をcredentialsへ設定 bin/rails credentials:editで以下を追加 mission_control: http_basic_auth_user: your_user http_basic_auth_password: your_password rails sを起動、http://localhost:3000/jobs へアクセス
Mission Control Jobs スクリーンショット
ジョブの定期実行 Solid Queueはcronのようなジョブを定期実行する仕組みも提供 スケジューラは solid_queue_recurring キューへジョブを投入 設定ファイル: config/recurring.yml config/recurring.yml production:
clear_solid_queue_finished_jobs: command: "SolidQueue::Job.clear_finished_in_batches(sleep_between_batches: 0.3)" schedule: every hour at minute 12 デフォルト設定: 「完了ジョブを消すジョブ」を12分ごとに繰り返し実行 各タスクはジョブのクラスまたはコマンドを指定 各タスクのschedule設定はfugit Gemの文法で書ける
ジョブ定期実行の細かい話 ジョブが重複してキューに入らないような仕組みが用意されている 複数マシンでアプリケーションサーバを起動しているケースを想定 同じ設定で複数のスケジューラが実行されるが、ジョブは重複しない ジョブ投入時のトランザクションでDBテーブルにレコード作成 solid_queue_recurring_executions テーブル task_key (タスク識別キー)と run_at
(実行時刻)でユニーク制約 結果、同時刻の同一タスクについては1つのジョブしか作成されない preserve_finished_jobs設定がtrue (デフォルト) のときに動作 スケジュール設定ファイルconfig/recurring.ymlは1つだけ 複数のスケジューラが同じスケジュールを使う 複数のスケジューラで異なる設定を使うことはできない
Solid Queue へ移行すべき? 非同期ジョブをこれから導入するならば便利で有力な選択肢 既にSidekiqなどを使っている場合、Solid Queue へ移行すべき? 参考: willnetさん Kaigi
on Rails 2024 "Sidekiq vs Solid Queue" https://kaigionrails.org/2024/talks/willnet/ 移行メリットがあるのは脱Redisしてサーバコスト削減するケースなど限定的かも 性能はSidekiqの方が良いが、Solid Queueもそれなりの性能を出せる SidekiqはSolidQueueの15倍速い Sidekiq wiki Solid Queueで2000万件/日のジョブを処理 on HEY Rails8.0 release note キュー監視、エラー監視などの移行も必要
Railsの練習帳 Solid Queue https://zenn.dev/igaiga/books/rails-practice-note/viewer/solid_queue Solid Queue 参考資料 Solid Queue README
https://github.com/rails/solid_queue Railsガイド Active Jobの基礎 https://railsguides.jp/active_job_basics.html TechRacho Rails: Mission Control Jobs gem README(翻訳) https://techracho.bpsinc.jp/hachi8833/2025_10_27/145131
宣伝: お仕事募集中 Railsの業務委託のお仕事を週1〜2日程で探してます ↓ 業務内容の例( 「こんなこともできる?」とご相談いただけましたら) ペアプロ屋、著書 パーフェクトRailsやRailsの練習帳などでの読書会や講義 書籍、ドキュメント、講演資料の書き方解説やレビュー Active
Job導入の設計相談や実装 RubyとRailsのバージョンアップ実装作業、レガシーコード改善実装 コードの健康診断とレポート CTO経験にもとづく経営相談 ガーネットテック373株式会社 業務内容詳細ページ、問い合わせページ ← こちらから気軽にご相談ください