LoginSignup
2
2

More than 3 years have passed since last update.

SidekiqをActive Jobから利用する [翻訳]

Posted at

SidekiqをActive Jobから利用する

最終更新 2019/09/18 編集者 Mike Perham

Rails 4.2からActive Jobが導入されました。Active Jobはジョブキューシステムのための標準的なインターフェースです。
Active JobはSidekiqと一緒に利用することができます。

Active Jobのセットアップ

Active Jobのアダプターとして :sidekiq を指定する必要があります。そうしないとデフォルトの :async という値になってしまいます。
この設定は config/application.rb に記載してください。

class Application < Rails::Application
  # ...
  config.active_job.queue_adapter = :sidekiq
end

新しいジョブの生成にはRailsのジェネレーター機能を使うことができます。

rails generate job Example

上記のコマンドを実行すると app/jobs/example_job.rb というファイルが作成されます。

class ExampleJob < ActiveJob::Base
  # defaultという名前のキューを利用する
  queue_as :default

  def perform(*args)
    # ジョブの中身
  end
end

使用方法

Sidekiqを単体で使う場合のジョブと同じように、どこからでもキューにジョブをプッシュすることができます。

ExampleJob.perform_later args

上記のコードを実行後すぐに、Sidekiqはジョブを実行します。何らかの理由でジョブが失敗した場合、Sidekiqは通常どおり再試行します。

エラーハンドリングをカスタマイズする

Active Jobは、Sidekiqの再試行機能を完全にはサポートしていません。
代わりに、特定の例外が発生したときに再試行するための抽象化された仕組みがあります。

class ExampleJob < ActiveJob::Base
  rescue_from(ErrorLoadingSite) do
    retry_job wait: 5.minutes, queue: :low_priority
  end

  def perform(*args)
    # Perform Job
  end
end

デフォルトのActive Jobの再試行方法は5秒間隔で3回です。
これが完了すると(15〜30秒後)、Active JobはSidekiqにジョブを戻します。Sidekiqはエクスポーネンシャルバックオフを使用して再試行を引き継ぎます。

Sidekiq 6.0.1以降では、ActiveJobsから sidekiq_options を使用することで、標準のSidekiq再試行メカニズムを利用できます。

class ExampleJob < ActiveJob::Base
  sidekiq_options retry: 5

  def perform(*args)
    # Perform Job
  end
end

Action Mailer

Action Mailerには、#deliver_later という非同期でメールを送信するためのメソッドが付属しています。(メールはバックグラウンドジョブで送信されます)。

Active JobがSidekiqを使用するように設定されている場合に限り、#deliver_later を使用できます。
Sidekiqだけを使用する場合とは異なり、Active Jobを使用すると、ActiveRecordインスタンスがグローバルIDでシリアライズされます。シリアライズされたレコードは後でデシリアライズされます。

メール送信ジョブは mailers というキューにプッシュされます。Sidekiqを起動する際にこのキューを指定することを忘れないようにしてください。

bundle exec sidekiq -q default -q mailers

メール送信ジョブをキューに送る基本的なコードは下記の通りです。

UserMailer.welcome_email(@user).deliver_later

ジョブキューに送らずに同期的にメールを送信するコードは下記の通りです。

UserMailer.welcome_email(@user).deliver_now

Sidekiqでは、遅延を設定してメールを送信するオプションがありました。Active Jobでも同様に遅延を設定することができます。

Sidekiqだけで遅延を設定するコードは下記の通りです。

UserMailer.delay_for(1.hour).welcome_email(@user.id)
UserMailer.delay_until(5.days.from_now).welcome_email(@user.id)

Active Job経由で同様のことを行うコードは下記の通りです。

UserMailer.welcome_email(@user).deliver_later(wait: 1.hour)
UserMailer.welcome_email(@user).deliver_later(wait_until: 5.days.from_now)

グローバルIDを使用する

RailsのグローバルID機能を使うことで、ActiveRecordオブジェクトを #perform の引数として渡す際にシリアライズすることができます。そのため、

def perform(user_id)
  user = User.find(user_id)
  user.send_welcome_email!
end

上記のコードは、下記のコードで置き換えることができます。

def perform(user)
  user.send_welcome_email!
end

残念なことに、上記のコードの違いによって、Active Jobを使う場合とそうでない場合で例外処理に違いが生じています。
ジョブがエンキューされる前までは User モデルのレコードが存在し、perform が実行される前にそのレコードが削除された場合、通常Sidekiqでは下記のように場合分けして処理します。

def perform(user_id)
  user = User.find_by(id: user_id)

  if user
    user.send_welcome_email!
  else
    # user レコードが削除されている場合の処理
  end
end

それがActive Jobを使用する場合は、User のインスタンスをデシリアライズする際に perform(user) がレコードが存在しないことに対する例外を raise するようになります。
その場合の例外処理は下記の通りです。

class MyJob < ActiveJob::Base
  rescue_from ActiveJob::DeserializationError do |exception|
    # user レコードが削除されている場合の処理
  end

  # ...
end

ジョブID

ActiveJobには、Sidekiqにとって意味のない独自のジョブIDがあります。 SidekiqのJIDを取得するには provider_job_id を使用します。

job = SomeJob.perform_later
jid = job.provider_job_id

パフォーマンス

ベンチマークによると、ActiveJobを使うとRedisへのジョブのプッシュがおよそ2〜20倍遅くなります。そして、ジョブを処理する際のオーバーヘッドが3倍ほどあります。(Rails 5.1.4 と Sidekiq 5.1.1 の場合)
この件については このイシュー のコメントを読んでください。

キュー名のプリフィックス

Active Jobでは、キュー名にプレフィックスを設定できます。 環境固有のプレフィックスを使用しないでください。
各環境では完全に別れた個別のRedisデータベースを使用する必要があります。
そうしないと、すべての環境で同じRetryセットとScheduledセットが共有され、混乱が生じる恐れがあります。

有償版のSidekiqとの併用

Sidekiq ProおよびSidekiq Enterpriseの機能の一部は、ActiveJobと一緒に使用しようとすると、予測しない壊れ方をする可能性があります。
たとえば、バッチ内でのActiveJobsの使用は基本的なユースケースでは機能しますが、ActiveJobの再試行メカニズムと併用しようとすると失敗します。
何か疑わしい挙動が起きた場合は、Active JobとSidekiqのネイティブなAPIを併用しないようにしてください。

2
2
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
2
2