実務でRailsアプリケーションを扱っており、非同期処理の実装でSidekiqを扱うことになりました。
Sidekiqを直接扱うパターンとActiveJob経由で扱うパターンの2通りあることを知ったため、それぞれのメリット/デメリットを調査しました。
参考にした記事
本記事を書くにあたり以下の記事を参考にさせていただきました。
はじめに
RailsアプリケーションへのSidekiqの導入手順は割愛します。
まず、Sidekiqを直接扱うパターンとは、以下のようにSidekiq:Workerモジュールを直接includeしたジョブクラスを定義した実装を指します。
class BackgroundJob
include Sidekiq::Worker
Sidekiq_options queue: "default"
def perform(id)
# 非同期実行したい内容
end
end
一方で、ActiveJob経由で扱うパターンとは、以下のようにApplicationJobを継承したジョブクラスを定義する実装を指します。
class BackgroundJob < ApplicationJob
queue_as :default
def perform(an_active_record_object)
# 非同期実行したい内容
end
end
メリット/デメリット比較
Sidekiqを直接扱う | ActiveJob経由で扱う | |
---|---|---|
メリット | ・高パフォーマンス ・Sidekiq固有機能が利用可 |
・引数にオブジェクトを指定可能 ・他のキューイングへの移行コスト小 |
デメリット | ・引数にオブジェクトを指定不可 ・他のキューイングへの移行コスト大 |
・Sidekiqの固有機能を使えない |
比較ポイントとしては大きく3点です。
1. パフォーマンス面
ActiveJobの公式ドキュメントによると、ActiveJobを介さずSidekiq等のキューイングをそのまま使用する場合、ジョブをRedisにpushする速度が2~20倍向上するとされています。
またSidekiqにはバルクキューイングという、同じジョブ大量に実行したい場合に効率的にキューイングできる仕組みもあります。ActiveJob経由で利用する場合はこうしたSidekiq固有の仕組みも利用できなくなります。
2. 引数の自由度
公式ドキュメントによると、Sidekiqに渡せる引数は 文字列、整数、浮動小数点数、真偽値、null(nil)、配列、およびハッシュとされており、オブジェクトを渡すことはできません。
そのため、ジョブの中で特定のActiveRecordオブジェクトに対する処理を実施したい場合は、以下のように引数でそのオブジェクトのIDを渡してジョブの中で検索をしてやる必要があります。
class BackGroundJob
include Sidekiq::Worker
def perform(user_id)
user = User.find(user_id)
user.do_hoge
end
end
一方でActiveJobはGlobalIDという仕組みをサポートしているため、以下のように引数にActiveRecordオブジェクトを直接指定することができます。
class BackGroundJob < ApplicationJob
def perform(user)
user.do_hoge
end
end
3. 他のキューイング(Shoryuken, Delayed_job等)への移行コスト
Sidekiqを直接扱う場合は、ジョブクラスにSidekiq固有のコードを書いていくことになるため、他のキューイングへの移行時のコストが大きくなります。一方でActiveJob経由の場合は、バックエンドのキューイングに関わらずコードは同じになるため、他のキューイングへの移行は行いやすくなります。ただし、キューイングを切り替えることはそこまで頻繁には起きないと考えられるため、そこまでメリット(デメリット)にはならないかと考えています。
最後に
上記の参考記事では、SidekiqかActiveJobかの二者択一ではなく共存させるのも一つの方法だと述べられています。私もこの考えに賛成です。
ハイパフォーマンスが求められるジョブやバルクキューイング等のSidekiq固有機能を利用したい場合は直接利用、ハイパフォーマンスは求められず引数にオブジェクトを指定したほうが実装がシンプルになる場合はActiveJob経由、のように使い分けられるとよいかと思います。