投稿日:2026/7/21
更新日:2026/7/21

Rails の非同期ジョブには「Sidekiq を直接扱うパターン」と「ActiveJob 経由で扱うパターン」の2通りがある。class ApplicationJob に include Sidekiq::Worker と書かれているコードを読んで、
「これは全ジョブの共通処理として機能するのか?」を調べたときのまとめ。
include Sidekiq::Worker している ApplicationJob は パターン1(Sidekiq 直接)。名前が Rails 標準の ActiveJob 基底クラスと同じなだけで、中身は別物。ApplicationJob を通るのは「それを継承したジョブ」だけで、直接 include したワーカー・ActiveJob 経由のジョブ(deliver_later 等)・gem 定義のワーカーはバイパスする。| パターン1: Sidekiq 直接 | パターン2: ActiveJob 経由 | |
|---|---|---|
| クラス定義 | include Sidekiq::Worker(新しめは Sidekiq::Job) |
class Foo < ApplicationJob < ActiveJob::Base |
| エンキュー | perform_async / perform_in |
perform_later / set(wait: ...) |
| 実行時の姿 | そのクラスがそのまま Sidekiq のワーカー | Sidekiq アダプタの JobWrapper に包まれて実行 |
| 引数 | JSON にシリアライズ可能な値のみ | GlobalID 対応(AR モデルをそのまま渡せる) |
| リトライ等の設定 | sidekiq_options |
retry_on / discard_on(ActiveJob の語彙) |
| キューアダプタ | 関係ない(常に Sidekiq) | config.active_job.queue_adapter に依存 |
# パターン1: Sidekiq 直接
class SyncUserWorker
include Sidekiq::Worker
sidekiq_options queue: :default, retry: 3
def perform(user_id)
...
end
end
SyncUserWorker.perform_async(user.id)
# パターン2: ActiveJob 経由
class SyncUserJob < ApplicationJob # < ActiveJob::Base
queue_as :default
retry_on Timeout::Error, attempts: 3
def perform(user) # モデルをそのまま渡せる
...
end
end
SyncUserJob.perform_later(user)
ActiveJob::Base を継承 → パターン2Sidekiq::Worker / Sidekiq::Job を include → パターン1ApplicationJob は本来 rails new が生成する ActiveJob の基底クラスの標準名。
# Rails 標準(パターン2の入口)
class ApplicationJob < ActiveJob::Base
end
今回のアプリはこの標準名を流用して、中身を Sidekiq 直接方式に置き換えている。
# 今回のアプリ(名前は標準だが中身はパターン1)
class ApplicationJob
include Sidekiq::Worker
end
名前だけ見ると ActiveJob(パターン2)に見えるので、コードリーディングでは名前ではなく中身で判断すること。
ApplicationJob に書いた共通処理(sidekiq_options、共通 rescue、ロギング等)が効くのは
それを継承したジョブクラスだけ。以下はすべてバイパスする。
class LegacyWorker
include Sidekiq::Worker # ApplicationJob を継承していない
...
end
個別クラスが直接 include していれば、基底クラスの共通処理は一切効かない。
ActiveJob ベースのジョブは ActiveJob::Base を継承し、Sidekiq アダプタのJobWrapper として実行されるため、Sidekiq 直接方式の ApplicationJob は通らない。
Rails 組み込みの非同期処理はここに含まれるので見落としやすい:
ActionMailer の deliver_laterdestroy_later など Rails 組み込みの非同期処理sidekiq-cron / sidekiq-scheduler が起動するジョブや、gem 側で定義された
Sidekiq ワーカーも独自クラスなので通らない。
Sidekiq プロセス
┌─────────────────────────────┐
perform_async ────→│ ApplicationJob 継承ワーカー │← 共通処理が効く
perform_async ────→│ 直接 include ワーカー │← 効かない
deliver_later ────→│ ActiveJob JobWrapper │← 効かない
sidekiq-cron ────→│ gem 定義ワーカー │← 効かない
└─────────────────────────────┘
# ApplicationJob を経由しないワーカーを探す
grep -rn "include Sidekiq::\(Worker\|Job\)" app/ lib/ | grep -v application_job
# ActiveJob ベースのジョブが混在していないか
grep -rn "ActiveJob::Base\|deliver_later" app/ lib/
# エンキュー方式の実態(perform_async ばかりならほぼ純粋なパターン1)
grep -rn "perform_async\|perform_in" app/ lib/
grep -rn "perform_later" app/ lib/
# ActiveJob のアダプタ設定(ActiveJob も Sidekiq に流れるか)
grep -rn "queue_adapter" config/
perform_async ばかりでもメール送信で deliver_later が1箇所あれば、
そこだけ ActiveJob 経由で動く混在構成になっている。
Sidekiq は公式にはワーカーの継承をあまり推奨していない
(sidekiq_options の継承挙動がバージョンによって異なるため)。
全ジョブに確実に効かせたい共通処理(ロギング、エラー通知、コンテキスト設定など)は、
継承ベースの共通化よりも Sidekiq のサーバーミドルウェアが確実。
ミドルウェアなら ActiveJob 経由・直接定義ワーカー含め、
その Sidekiq プロセスで実行される全ジョブを通る。
# config/initializers/sidekiq.rb
class JobLoggingMiddleware
include Sidekiq::ServerMiddleware
def call(worker, job, queue)
# 全ジョブ共通の前処理
yield
# 全ジョブ共通の後処理
rescue => e
# 全ジョブ共通のエラーハンドリング
raise
end
end
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.add JobLoggingMiddleware
end
end
| 共通化の手段 | 効く範囲 |
|---|---|
ApplicationJob 継承 |
継承したジョブのみ |
| サーバーミドルウェア | その Sidekiq プロセスの全ジョブ(ActiveJob 経由・gem 定義含む) |
| 観点 | Sidekiq 直接が有利 | ActiveJob 経由が有利 |
|---|---|---|
| 性能 | ◎ ラッパーがない分オーバーヘッド小 | JobWrapper 経由の分やや重い |
| Sidekiq 固有機能 | ◎ sidekiq_options をフルに使える |
一部使えない / 書き方が変わる |
| バックエンド差し替え | 不可(Sidekiq 固定) | ◎ アダプタ変更だけで移行可能 |
| 引数の柔軟さ | JSON 化できる値のみ | ◎ GlobalID でモデルを直接渡せる |
| Rails 標準との親和性 | deliver_later 等と方式が分かれる |
◎ Rails 組み込み機能と同じ経路 |
すでにパターン1で統一されているアプリなら無理に移行する必要はないが、
「実は deliver_later だけパターン2で動いている」ような混在に気づかず
共通処理が漏れる事故が起きやすい。上記 5 章の grep で実態を把握しておくとよい。