Rails: Mission Control Jobs gem README(翻訳)
mission_control-jobs gemは、Active JobアダプタにRailsベースのフロントエンドを提供します。現在はResqueとSolid Queueをサポートしています。mission_control-jobsの機能は、アダプタ自体が提供する機能にもよりますが、少なくとも、ジョブキューや現在のキューで待機中のジョブを確認したり、失敗したジョブを調べて再試行または破棄したりできます。
🔗 インストール方法
Gemfileに以下の行を追加します。
gem "mission_control-jobs"
続いて以下を実行します。
$ bundle install
🔗 基本的な設定
routes.rb
ファイルに以下を記述することで、mission_control-jobsのエンジンをアプリからアクセス可能な場所にマウントできます。
Rails.application.routes.draw do
# ...
mount MissionControl::Jobs::Engine, at: "/jobs"
設定はこれでおしまいです。たったこれだけで、ブラウザからmission_control-jobsのUIにアクセスして、既存のキューや、保留中のジョブ、さまざまなステータスのジョブを表示したり、失敗したジョブの破棄や再試行を行ったりできるようになります。
🔗 認証とベースコントローラクラス
デフォルトでは、mission_control-jobsのコントローラがホストアプリのApplicationController
をextend
します。認証が強制されていない場合は、ブラウザで/jobs
にアクセスすることで誰でもUIを表示できます。
アプリに何らかの認証機能を実装する必要が生じた場合は、以下のように別のクラスをmission_control-jobsのコントローラのベースクラスとして指定することで、実装をやりやすくできます。
Rails.application.configure do
MissionControl::Jobs.base_controller_class = "AdminController"
end
または、環境設定ファイルやapplication.rb
で以下を指定することも可能です。
config.mission_control.jobs.base_controller_class = "AdminController"
🔗 その他の設定項目
MissionControl::Jobs
やconfig.mission_control.jobs
では、上述のbase_controller_class
に加えて以下の項目も設定できます。
logger
- mission_control-jobsで利用したいロガーを指定します。デフォルトは
ActiveSupport::Logger.new(nil)
(ログ出力しない)です。なお、Active JobのロガーやActive Jobのバックエンドで設定されているロガーは、これと別物なのでご注意ください。 delay_between_bulk_operation_batches
- ジョブの
discard all
やretry all
(デフォルト)のような一括処理の実行中、バッチとバッチの間の待ち時間を指定します。デフォルトは0
です。 adapters
- mission_control-jobsで利用・
extend
するアダプタのリストを指定します。デフォルトでは、active_job.queue_adapter
で設定したアダプタが使われます。 internal_query_count_limit
- アダプタでカウントクエリを制限する必要がある場合、カウントするレコード数の上限を指定します。この値を超えると
INFINITY
が返されるようになります。これによりカウントクエリが高速化されます。デフォルトは500,000
です。 scheduled_job_delay_threshold
- スケジュールされたジョブが「遅延している」と判断するまでの時間を指定します。デフォルトは
1.minute
です(この場合、scheduled
ステータスがスケジュールされた時刻から1分以上経過してもステータスが変わらない場合は、遅延しているとみなされます)。 show_console_help
- コンソールヘルプを表示するかどうかを指定します(デフォルトは
true
)。コンソールのヘルプメッセージを表示したくない場合はfalse
に設定します。
このライブラリは、クエリインターフェイスと以下の設定を用いてActive Jobを拡張します。
config.active_job.default_page_size
- Active Jobが背後のアダプタにクエリを送信するときの内部バッチサイズと、上で定義した一括操作のバッチサイズを指定します(デフォルトは
1000
)。
🔗 指定可能なアダプタ
- Resque
- キューの一時停止は、プロジェクトに
resque-pause
がインストール済みの場合にのみ可能です。 - Solid Queue
- 必須バージョン: 0.9以上
🔗 高度な設定
私たちがmission_control-jobsを構築したときは、複数のアプリのバックエンドを1つのアプリから管理することで、監視やアラートなどアプリ関連のあらゆる作業を一元化することを構想していました。
しかしアプリによっては2つ以上のデータセンターで実行され、それぞれが異なるRedis設定に沿ってさまざまなResqueインスタンスを実行することもありえます。このため、「複数アプリのサポート」と「1アプリで複数アダプタの利用のサポート」を両方追加してあります。
私たちはResqueからSolid Queueに移行したので、単一のデータセンターでキュー管理用アプリ内からmission_control-jobsを実行する場合であっても、mission_control-jobsがサポートする両方のアダプタを管理する必要があります。
mission_control-jobsの上述の基本設定に追加設定を行わない場合は、「1つのアプリ」と「active_job.queue_adapter
で設定済みの1つのサーバー」という構成で設定されます。
複数アダプタをサポートしたい場合は、上述したadapters
設定で以下のようにアダプタをmission_control-jobsに追加する必要があります。
config.mission_control.jobs.adapters = [ :resque, :solid_queue ]
続いて、複数アプリまたは複数サーバー向けの設定を行います。この設定は以下のようにイニシャライザファイルに書けます(これはテスト用のダミーアプリから取り出したものです)。
require "resque"
require "resque_pause_helper"
require "solid_queue"
Resque.redis = Redis::Namespace.new "#{Rails.env}", redis: Redis.new(host: "localhost", port: 6379)
SERVERS_BY_APP = {
BC4: %w[ resque_ashburn resque_chicago ],
HEY: %w[ resque solid_queue ]
}
def redis_connection_for(app, server)
redis_namespace = Redis::Namespace.new "#{app}:#{server}", redis: Resque.redis.instance_variable_get("@redis")
Resque::DataStore.new redis_namespace
end
SERVERS_BY_APP.each do |app, servers|
queue_adapters_by_name = servers.collect do |server|
queue_adapter = if server.start_with?("resque")
ActiveJob::QueueAdapters::ResqueAdapter.new(redis_connection_for(app, server))
else
ActiveJob::QueueAdapters::SolidQueueAdapter.new
end
[ server, queue_adapter ]
end.to_h
MissionControl::Jobs.applications.add(app, queue_adapters_by_name)
end
この設定例は、BC4とHEYという2つの異なるアプリを対象としており、それぞれにサーバーが2つずつ存在しています。BC4アプリにはそれぞれ設定の異なるResqueサーバーが2つ、HEYアプリにはResqueサーバーが1つとSolid Queueサーバーが1つずつ存在しています。
現時点でmission_control-jobsでサポートされているSolid Queue設定は1種類だけですが、今後複数のSolid Queueバックエンド(かつ異なるデータベースをサポート)を利用可能にする計画があります(#35)。
ResqueからSolid Queueへ、またはその逆の移行を行う場合は、以下のようにResqueとSolid Queueを同時に設定できます。
queue_adapters_by_name = {
resque: ActiveJob::QueueAdapters.lookup(:resque).new, # これによってResque.redisをRedisクライアントとして使う
solid_queue: ActiveJob::QueueAdapters.lookup(:solid_queue).new
}
MissionControl::Jobs.applications.add("hey", queue_adapters_by_name)
複数アプリや複数サーバーが設定されていれば、以下のようにメニューで切替可能になります。
🔗 基本的なUI利用法
既に述べたようにアダプタごとに機能が異なっているため、mission_control-jobsで利用できる機能も、利用するアダプタごとに異なります。
UI画面では、キューやジョブの表示や、失敗したジョブの破棄やリトライの他にも、さまざまなステータスの表示、キュー名やジョブクラス名を指定した絞り込み(今後フィルタを増やす構想もあります: #30)、キューの一時停止と再開、どのジョブがどのワーカーで実行されているかの確認、特定のジョブやワーカーのチェックといった、利用中のアダプタでサポートされている機能が使えます。
Queues
タブ(デフォルト)
In-proggress jobs
タブ
Workers
タブ
- 単一ジョブの詳細表示
- 単一ワーカーの詳細表示
🔗 コンソールヘルパー、スクリプティング、多数のジョブセットの処理
mission_control-jobsは、上述のWeb UIに加えて、アプリケーションとアダプタを切り替えられる軽量のコンソールヘルパーも提供されています。Web UIでは、破壊的になる可能性がある一部の操作(例: 失敗していないジョブの削除)は利用できないようになっています(ただし将来変更される可能性があります)。
コンソールでは、自分が行おうとしている操作を十分理解していれば、そうした操作をいつでも実行できます。
「Web UIでは管理しきれない膨大なジョブセットを扱う」「問題解決、クリーンアップ、データ移行用のスクリプトを書く」といった操作が必要になる場合があります。こういうときは、Active Jobを拡張したコンソールヘルパーとクエリAPIが役に立ちます。
まず、Railsコンソールに接続すると以下のような新しいメッセージが表示されます。
bin/rails c
Type 'jobs_help' to see how to connect to the available job servers to manage jobs
jobs_help
と入力すると、アプリケーションとアダプタを切り替える方法がわかりやすく表示されます。
>> jobs_help
You can connect to a job server with
connect_to "<app_id>:<server_id>"
Available job servers:
* bc4:resque_ashburn
* bc4:resque_chicago
* hey:resque
* hey:solid_queue
続いて以下を実行します。
>> connect_to "hey:solid_queue"
Connected to hey:solid_queue
これで、そのアダプタにあるジョブをクエリしたり操作したりする準備が整いました。クエリの例をいくつか示します。
# すべてのジョブを表示する
ActiveJob.jobs
# 失敗したジョブをすべて表示する
ActiveJob.jobs.failed
# some_queueにある保留中のジョブをすべて表示する
ActiveJob.jobs.pending.where(queue_name: "some_queue")
# 指定したジョブクラスの、失敗したジョブをすべて表示する
ActiveJob.jobs.failed.where(job_class_name: "SomeJob")
# 指定したジョブクラスにlimitとoffsetを指定して、保留中のジョブをすべて表示する
ActiveJob.jobs.pending.where(job_class_name: "SomeJob").limit(10).offset(5)
# (以下のステータスはアダプタでサポートされている前提)
# 指定のジョブクラスで、「スケジュール済み」「実行中」「完了済み」ジョブをすべて表示する
All scheduled/in-progress/finished jobs of a given class
ActiveJob.jobs.scheduled.where(job_class_name: "SomeJob")
ActiveJob.jobs.in_progress.where(job_class_name: "SomeJob")
ActiveJob.jobs.finished.where(job_class_name: "SomeJob")
# (ワーカーを指定してフィルタする機能がアダプタでサポートされている前提)
# 指定のワーカーで実行中のジョブをすべて表示する
ActiveJob.jobs.in_progress.where(worker_id: 42)
一括操作の例をいくつか示します。
# すべてのジョブをリトライする(失敗したジョブに対してのみ有効)
ActiveJob.jobs.failed.retry_all
# 指定のジョブクラスですべてのジョブをリトライする(失敗したジョブに対してのみ有効)
ActiveJob.jobs.failed.where(job_class_name: "SomeJob").retry_all
# 失敗したジョブをすべて破棄する
ActiveJob.jobs.failed.discard_all
# 指定のジョブクラスで保留中のジョブをすべて破棄する
ActiveJob.jobs.pending.where(job_class_name: "SomeJob").discard_all
# または指定のキューで保留中のジョブをすべて破棄する
ActiveJob.jobs.pending.where(queue_name: "some-queue").discard_all
これらの一括処理をコンソールで実行すると、バッチとバッチの間に2秒の待ち時間が発生します。この待ち時間は以下のようにdelay_between_bulk_operation_batches
で設定できます。
MissionControl::Jobs.delay_between_bulk_operation_batches = 5.seconds
🔗 貢献について
貢献に関心を持っていただきありがとうございます!このアプリをローカルで実行するには、以下のコマンドを実行するだけで済みます。
bin/setup
これによって、多数のジョブがseedとして読み込まれます。
mission_control-jobsには単体テスト、機能テスト、システムテストがあります。システムテストを実行するには、ChromeDriverをインストールしておく必要があります。
続いて以下のコマンドでテストを実行できます。
bin/rails test test/system
🔗 ライセンス
このgemは、MIT Licenseの条項に基づいてオープンソースとして利用可能です。
概要
MITライセンスに基づいて翻訳・公開いたします。
)
本文のライブラリ名はmission_control-jobsで統一しました。
参考: 週刊Railsウォッチ20240402: solid_queueとmission_control-jobsがRailsのリポジトリに追加された