はじめに
Sidekiqを使ったバックグラウンドジョブ処理を実装していると、「ワーカーはどうやってRedisからジョブを取得しているんだろう?」と疑問に思うことはありませんか?
よくある誤解として「ワーカーが定期的にRedisにジョブがあるかチェックしている」というものがありますが、実際は全く違う効率的な仕組みが使われています。
誤解されがちなポーリング方式
多くの人が想像するワーカーの動作:
# 間違った理解(実際はこうではない)
loop do
job = redis.get("queue:default")
if job
execute_job(job)
else
sleep(0.001) # 0.1ms待機
end
end
この方式だと以下の問題があります:
- 🚫 CPU使用率が高い: 常にRedisに問い合わせ
- 🚫 ネットワーク負荷: 無駄な通信が大量発生
- 🚫 レスポンス遅延: ポーリング間隔分の遅延が発生
- 🚫 リソース無駄遣い: ジョブがなくてもCPU・ネットワークを消費
実際のSidekiq: BRPOP(Blocking Right Pop)
SidekiqはBRPOPというRedisコマンドを使用しています。
BRPOPとは?
BRPOP queue:default 5
# queue:defaultからジョブを取得
# ジョブがない場合は最大5秒間ブロック(待機)
実際の動作フロー
パターン1: ジョブが追加された場合
- ワーカーがRedisに
BRPOP queue:default 5
を送信 - Redisはジョブがない場合、接続を保持したまま待機状態に
- 他のプロセス(Railsアプリなど)がジョブをキューに追加
- Redis が待機中のワーカーに即座にジョブデータを返却
- ワーカーがジョブを実行
- ジョブ完了後、再度
BRPOP queue:default 5
で次のジョブを待機
パターン2: タイムアウトの場合
- ワーカーがRedisに
BRPOP queue:default 5
を送信 - 5秒間ジョブが追加されない
- Redis が
nil
を返してタイムアウト - ワーカーは即座に再度
BRPOP queue:default 5
を実行 - 再び最大5秒間待機状態に
BRPOPの特徴
1. 持続的な接続
ワーカー ────────────── Redis
^この接続を維持
(ポーリングとは違い、接続したまま待機)
2. リアルタイム性
# アプリケーション側
MyWorker.perform_async(user_id) # ジョブをキューに追加
# ↓ 即座に(0.1ms後とかではなく)
# ワーカー側
# BRPOPで待機中のワーカーが即座にジョブを取得して実行開始
3. 複数ワーカーでの競合回避
# 3つのワーカーが同じキューを監視
ワーカー1: BRPOP queue:default 5 # 待機中
ワーカー2: BRPOP queue:default 5 # 待機中
ワーカー3: BRPOP queue:default 5 # 待機中
# ジョブが1つ追加されると...
# → Redis が自動的に1つのワーカーにのみジョブを渡す
# → 他のワーカーは引き続き待機
実装レベルでの確認
Sidekiqの現在のソースコード(2025年時点)を確認すると、実際にBRPOPを使用していることが確認できます:
# sidekiq/lib/sidekiq/processor.rb
def get_one
uow = capsule.fetcher.retrieve_work
# ...
end
# sidekiq/lib/sidekiq/fetch.rb - BasicFetch クラス
def retrieve_work
qs = queues_cmd
if qs.size <= 0
sleep(TIMEOUT)
return nil
end
queue, job = redis { |conn| conn.blocking_call(TIMEOUT, "brpop", *qs, TIMEOUT) }
UnitOfWork.new(queue, job, config) if queue
end
重要なポイント:
- BasicFetch クラスの
retrieve_work
メソッドでconn.blocking_call(TIMEOUT, "brpop", *qs, TIMEOUT)
を使用 - TIMEOUT は 2秒に設定されており、2秒ごとにプロセス終了チェックを行う
- Sidekiqの公式ドキュメントでも「Sidekiq uses BRPOP to fetch a job from the queue in Redis」と明記
パフォーマンス比較
方式 | CPU使用率 | ネットワーク負荷 | リアルタイム性 | 実装複雑性 |
---|---|---|---|---|
ポーリング | 高い | 高い | 遅延あり | 簡単 |
BRPOP | 低い | 低い | 即座 | 適度 |
実際に確認してみる
Sidekiqの管理画面(/sidekiq/busy
)で現在の状況を確認できます:
Processes タブ:
- 各ワーカープロセスの状態
- Busy: 現在実行中のジョブ数
- Threads: 使用スレッド数
Busy タブ:
- 現在実行中のジョブ詳細
- どのワーカーが何を実行中か
まとめ
Sidekiqのワーカーは:
✅ ポーリングしない: 定期的なチェックは行わない
✅ BRPOPを使用: Redisとの持続的接続で効率的にジョブを取得
✅ リアルタイム処理: ジョブ追加と同時に処理開始
✅ リソース効率: CPU・ネットワークリソースを無駄遣いしない
この仕組みにより、Sidekiqは高いパフォーマンスとスケーラビリティを実現しています。バックグラウンドジョブの設計時には、この効率的な仕組みを理解しておくことで、より良いアーキテクチャを構築できるでしょう。