LoginSignup
18
8

More than 3 years have passed since last update.

ActiveJobとSidekiqを組み合わせたときに、ジョブ単位のオプションはどこまで設定できるか?

Last updated at Posted at 2019-10-23

背景

非同期処理

一般的なブラウザは、送信したHTTPリクエストが30秒以内に応答されない時はタイムアウトし、そのHTTPリクエストは失敗したと判定します。Webアプリケーションが長時間かかる処理を行いたい場合は、ブラウザがタイムアウトしないように、HTTPリクエストに応答を一旦返し、その後実際の処理を実行します。このような処理をリクエストの応答と実処理の完了が同期していない(同時でない)点から、非同期処理と呼びます。

非同期処理フレームワーク

非同期処理では、一つ一つの処理をジョブと呼びます。Webアプリケーションは、HTTPリクエストを受け取ると、次のように動作します。

  1. リクエストに応じたジョブを作成
  2. ジョブをキューと呼ばれる領域に保存
  3. HTTPリクエストに応答を返す

非同期処理にはワーカーという実行プログラムがあります。ワーカーは、キューを監視し、キューにジョブが保存されたら、キューからジョブを取り出し、そのジョブを実行します。

ワーカーの処理が終わる前に新しいリクエストが来た場合、Webアプリケーションは新しいジョブをキューに保存します。キューには一定数のジョブが保存でき、ワーカーは実行中のジョブが完了したら、次のジョブがないかキューを確認します。キューが存在するおかげでWebアプリケーションは、ワーカーの実行状態を気にせずにジョブを作成できます。

一般にキューに入れられたジョブは先入れ先出しで処理されます。このためキューはキューと呼ばれるます。

以下の機能をまとめて提供するプログラムを非同期処理フレームワークと呼びます。

  • ジョブを定義するフォーマット
  • キューの実装
  • ワーカーの実装
  • キューとワーカーを管理する機能

ActiveJobとSidekiq

Ruby on RailsというWebアプリケーションフレームワークには、非同期処理を実行するためにActiveJobという機能があります。ActiveJobでは、処理を実行する非同期処理フレームワークをいくつかの候補から選択することが出来ます。その1つにSidekiqがあります。

SidekiqはRubyで書かれた非同期処理フレームワークです。Ruby on Railsと関わりなく広く使われています1。Sidekiq自身には豊富な機能がありますが、ActiveJobから利用する場合にはいくつかの制限があります。その1つにsidekiq_optionsメソッドを使った、ジョブ単位のオプション設定があります。

ジョブ単位のオプション設定

Sidekiqを、ActiveJobを経由せずに、直接使う時は、Sidekiq::Workerというクラスを継承してジョブを実装します。Sidekiq::Workerにはsidekiq_optionsメソッドがあります。sidekiq_optionsを使うとジョブを登録するキューや、ジョブが失敗したときのリトライ処理の有無が設定できます。次の4項目を設定できます。

  • queue
  • retry
  • backtrace
  • pool

詳しくはAdvanced Options · mperham/sidekiq Wikiを読んでください。

命題

ActiveJobからSidekiqを使う際に、ジョブにsidekiq_optionsを設定できるでしょうか?

結論

Sidekiq 6.0.1 + Rails 6.0.2の組合せで、できるようになるらしい。

Sidekiq 6.0.1

As of Sidekiq 6.0.1 you can sidekiq_options in your ActiveJobs and configure the standard Sidekiq retry mechanism.

翻訳すると...

Sidekiq 6.0.1以降、ActiveJobsでsidekiq_optionsを使用して、標準のSidekiq再試行メカニズムを構成できます。

Rails 6.0.2

Oh and you need Rails master too, it needs Rails 6.0.1.

翻訳すると...

ああ、Railsのmasterブランチも必要です。Rails6.0.1が必要です。

その後、該当コミットがRails 6.0.1に入らなかったため、6.0.2が必要になりました。

Update: support code will only make it to Rails 6.0.2

その他の悪あがき

現時点で、ActiveJobからsidekiq_optionsは設定できませんが、ジョブ単位にいくつかの振る舞いを変更することが出来ます。

queue

ActiveJobからqueue_asメソッドを使えばジョブを登録するキューを指定することが出来ます。Sidekiqではキュー毎にジョブのチェック頻度が設定できます。これを使ってジョブ単位の優先順位を設定できます。
詳しくはActive Job の基礎 - Rails ガイドAdvanced Options · mperham/sidekiq Wikiを読んでください。

retry

リトライ回数を増やす

Sidekiqではリトライ回数を設定することが出来ます。ジョブが失敗した時に何回リトライするかを設定します。ジョブ単位の設定ではありませ。すべてのジョブで共通の設定です。
詳しくはError Handling · mperham/sidekiq Wikiを読んでください。

Sidekiqでのリトライ回数に、ActiveJobでのリトライ回数を追加できます。

Active Job · mperham/sidekiq Wiki によると

The default AJ retry scheme is 3 retries, 5 seconds apart. Once this is done (after 15-30 seconds), AJ will kick the job back to Sidekiq, where Sidekiq's retries with exponential backoff will take over.

翻訳すると...

「デフォルトのActiveJob再試行スキームは、5秒間隔で3回再試行されます。これが完了すると(15〜30秒後)、ActiveJobはSidekiqにジョブをキックバックします。Sidekiqは指数関数的なバックオフを使用した再試行を引き継ぎます。」

ActiveJobではretry_onメソッドを使ってリトライ回数を設定できます。例えば、次のジョブは3回リトライします。2

class AlwaysFailJob < ApplicationJob
  retry_on ZeroDivisionError, attempts: 3

  def perform(params)
    p "run job !!!"
    1/0
  end
end

このジョブを実行すると、ActiveJobが3回リトライしたあとで、Sidekiqは既定回数リトライをします。
つまり、ActiveJobがリトライする回数分、リトライ数を増やせます。

リトライ回数を0にする

ActiveJobはdicard_onメソッドを使って、特定の例外が起きた時に、ジョブを成功したことに出来ます。例えば、次のジョブは実行するとZeroDivisionErrorが起きますが、成功します。

class AlwaysSuccessJob < ApplicationJob
  discard_on ZeroDivisionError

  def perform(params)
    p "run job !!!"
    1/0
  end
end

ジョブは成功するので、Sidekiqはリトライしません。

backtrace

ジョブの失敗時に例外のbacktrace(Ruby以外の言語ではスタックトレースとも呼ばれます)を保存して、Sidekiqの管理UIから参照するためオプションです。このオプションはActiveJobから設定出来ません。

pool

SidekiqはRedisというインメモリデータベースをつかって、ジョブを保存するキューを実装しています。

ジョブを登録するときに使用するRedisへのコネクションプールを指定するオプションです。このオプションはActiveJobから設定出来ません。

このオプションの使い道は知りません。

Sidekiq::Workerを使う

つまりActiveJobを使うのをやめます。

RSpecでテストを書く時にActiveJob::TestHelperが使えません。3

代わりにSidekiq::Testingを使います。例えば、次のようにテストを書きます。

require 'rails_helper'
require 'sidekiq/testing'
Sidekiq::Testing.fake!

describe ExampleJob do
  it do
    expect {
      ExampleJob.perform_async nil
    }.to change(ExampleJob.jobs, :size).by(1)
  end
end

詳しくはTesting · mperham/sidekiq Wikiを読んでください。

参考


  1. たとえばRubyGems.orgからは6000万回ダウンロードされています。 

  2. retry_onはRails 5.1.0で追加された機能です。それ以前のバージョンのActiveJobを使う場合は自分で実装する必要があります。ActiveJobでリトライ制御 - Qiitaなどを参考にしてください。 

  3. ActiveJob::TestHelperの使用例が知りたい方はRSpec でキューイングした ActiveJob を同期実行する - QiitaDelayed Job Queue Adapter in RSpec with Rails 5.1 - Today I Learnedを読んでください。 

18
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
8