YASD-TECH
YASD TECH
# Rails

SidekiqとActiveJobの2パターンとApplicationJobの罠

投稿日:2026/7/21

更新日:2026/7/21

ttitleImage

Sidekiq と ActiveJob の2パターンと ApplicationJob の罠

Rails の非同期ジョブには「Sidekiq を直接扱うパターン」と「ActiveJob 経由で扱うパターン」の2通りがある。
class ApplicationJobinclude Sidekiq::Worker と書かれているコードを読んで、
「これは全ジョブの共通処理として機能するのか?」を調べたときのまとめ。


1. 結論

  • include Sidekiq::Worker している ApplicationJobパターン1(Sidekiq 直接)。名前が Rails 標準の ActiveJob 基底クラスと同じなだけで、中身は別物。
  • 全ジョブがそこを通るとは限らない。 ApplicationJob を通るのは「それを継承したジョブ」だけで、直接 include したワーカー・ActiveJob 経由のジョブ(deliver_later 等)・gem 定義のワーカーはバイパスする。
  • 共通処理を全ジョブに確実に効かせたいなら、継承ではなく Sidekiq のサーバーミドルウェアを使う。

2. 2つのパターン

パターン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)

見分け方は「クラス名」ではなく「継承 / include の中身」

  • ActiveJob::Base を継承 → パターン2
  • Sidekiq::Worker / Sidekiq::Job を include → パターン1

3. ApplicationJob という名前の罠

ApplicationJob は本来 rails new が生成する ActiveJob の基底クラスの標準名

# Rails 標準(パターン2の入口)
class ApplicationJob < ActiveJob::Base
end

今回のアプリはこの標準名を流用して、中身を Sidekiq 直接方式に置き換えている。

# 今回のアプリ(名前は標準だが中身はパターン1)
class ApplicationJob
  include Sidekiq::Worker
end

名前だけ見ると ActiveJob(パターン2)に見えるので、コードリーディングでは名前ではなく中身で判断すること。


4. 「全ジョブが ApplicationJob を通る」とは限らない

ApplicationJob に書いた共通処理(sidekiq_options、共通 rescue、ロギング等)が効くのは
それを継承したジョブクラスだけ。以下はすべてバイパスする。

(1) 直接 include しているワーカー

class LegacyWorker
  include Sidekiq::Worker   # ApplicationJob を継承していない
  ...
end

個別クラスが直接 include していれば、基底クラスの共通処理は一切効かない。

(2) ActiveJob 経由のジョブ

ActiveJob ベースのジョブは ActiveJob::Base を継承し、Sidekiq アダプタの
JobWrapper として実行されるため、Sidekiq 直接方式の ApplicationJob は通らない。
Rails 組み込みの非同期処理はここに含まれるので見落としやすい:

  • ActionMailerdeliver_later
  • Active Storage の解析・パージ用ジョブ
  • destroy_later など Rails 組み込みの非同期処理

(3) gem が定義するワーカー

sidekiq-cron / sidekiq-scheduler が起動するジョブや、gem 側で定義された
Sidekiq ワーカーも独自クラスなので通らない。

                    Sidekiq プロセス
                    ┌─────────────────────────────┐
 perform_async ────→│ ApplicationJob 継承ワーカー   │← 共通処理が効く
 perform_async ────→│ 直接 include ワーカー        │← 効かない
 deliver_later ────→│ ActiveJob JobWrapper        │← 効かない
 sidekiq-cron  ────→│ gem 定義ワーカー             │← 効かない
                    └─────────────────────────────┘

5. 実コードでの調べ方

# 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 経由で動く混在構成になっている。


6. 共通処理を全ジョブに効かせたいならミドルウェア

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 定義含む)

7. どちらのパターンを選ぶか(ざっくり)

観点 Sidekiq 直接が有利 ActiveJob 経由が有利
性能 ◎ ラッパーがない分オーバーヘッド小 JobWrapper 経由の分やや重い
Sidekiq 固有機能 sidekiq_options をフルに使える 一部使えない / 書き方が変わる
バックエンド差し替え 不可(Sidekiq 固定) ◎ アダプタ変更だけで移行可能
引数の柔軟さ JSON 化できる値のみ ◎ GlobalID でモデルを直接渡せる
Rails 標準との親和性 deliver_later 等と方式が分かれる ◎ Rails 組み込み機能と同じ経路

すでにパターン1で統一されているアプリなら無理に移行する必要はないが、
「実は deliver_later だけパターン2で動いている」ような混在に気づかず
共通処理が漏れる事故が起きやすい。上記 5 章の grep で実態を把握しておくとよい。

Index

  • Sidekiq と ActiveJob の2パターンと ApplicationJob の罠
  • 1. 結論
  • 2. 2つのパターン
  • 見分け方は「クラス名」ではなく「継承 / include の中身」
  • 3. ApplicationJob という名前の罠
  • 4. 「全ジョブが ApplicationJob を通る」とは限らない
  • (1) 直接 include しているワーカー
  • (2) ActiveJob 経由のジョブ
  • (3) gem が定義するワーカー
  • 5. 実コードでの調べ方
  • 6. 共通処理を全ジョブに効かせたいならミドルウェア
  • 7. どちらのパターンを選ぶか(ざっくり)