Sidekiq について調べてみた
業務で必要に迫られて Sidekiq について調べてみたのでそのメモ。
公式ドキュメント
Simple, efficient background processing for Ruby.
Sidekiq uses threads to handle many jobs at the same time in the same process. It does not require Rails but will integrate tightly with Rails to make background processing dead simple.
Ruby のためのシンプルで効率的なバックグラウンドプロセスです。Sidekiq は同じプロセス内でスレッドを使用してたくさんのジョブを一度に処理することができます。Sidekiq は Rails を必要としませんが、バックグラウンドプロセスをとても簡単にするために、Rails と緊密に連携します。
という感じ。dead simple
という言い回しは知らなかった。英語の勉強にもなった。
このあたりは読んだ。
頭の整理
バックグラウンドプロセス実行のために必要な 3 つのもの
The Basics から。
1. Client
バックグラウンドで処理するための job を作る。
MyWorker.perform_async("Alice")
job は JSON 文字列として Redis に enqueue される。引数はシンプルな JSON datatype にする。
2. Redis
job のデータストレージ。
3. Server
Redis から job を dequeue する。その job に基づいて worker クラスを初期化し perform
メソッドを実行する。
job と worker
ChatGPT さまさまmm
job
-
定義:
Sidekiq で queue に登録されたタスクの単位。- enqueue されてから dequeue され実行されるまで、job として扱われる。
- 実際には、job は Redis に保存されるデータ構造として存在する。
-
構成要素:
job には以下のような情報が含まれる:- worker クラス名: 実際にその job を処理する worker クラス。
- 引数: job を実行する際に必要なデータ。
-
メタデータ: ジョブの状態管理や再試行に必要な情報。
- 例:
jid
(ジョブID)、キュー名、リトライ情報、スケジュールされた実行時刻(遅延ジョブの場合)など。
- 例:
-
例:
{ "class": "MyWorker", "args": ["example_arg"], "jid": "123abc", "queue": "default", "retry": true, "created_at": 1672512345 }
これは Redis に格納される job の形式で、Sidekiq が処理時に使用する。
worker
-
定義:
worker は、job を実行するためのロジックを持つ Ruby クラス。- Sidekiq::Worker モジュールをインクルードして実装する。
- 各 worker クラスは具体的なタスク(ビジネスロジック)を定義する。
-
役割:
workerクラスは以下を定義する:-
タスク(ビジネスロジック):
perform
メソッド内に処理内容を記述する。 - キュー設定: 使用するキュー名やリトライの設定を指定できる。
-
タスク(ビジネスロジック):
-
例:
class MyWorker include Sidekiq::Worker sidekiq_options queue: 'custom_queue', retry: 5 def perform(arg) puts "Processing: #{arg}" end end
違いを整理
項目 | job | worker |
---|---|---|
役割 | キューに登録されるタスクデータ | job を実行するためのロジックを定義 |
データ形式 | JSON 形式で Redis に格納 | Ruby クラスとしてコード内に定義 |
実行場所 | Redis 上で管理される | Sidekiq プロセス内で実行される |
内容 | worker 名、引数、メタデータ |
perform メソッドで具体的な処理を記述 |
関係性 | worker クラスを元に生成される | job を処理するために動作するクラス |
例で理解する: job と worker の流れ
-
worker の定義:
class MyWorker include Sidekiq::Worker def perform(name) puts "Hello, #{name}!" end end
-
job をエンキュー:
MyWorker.perform_async("Alice")
- ここで、
MyWorker
を基にした job が作成され、Redis に以下のようなデータが登録される:{ "class": "MyWorker", "args": ["Alice"], "queue": "default", "retry": true }
- ここで、
-
job が dequeue され実行:
- Sidekiq プロセス が job をデキューし、
MyWorker#perform("Alice")
を実行する。
- Sidekiq プロセス が job をデキューし、
補足 1: job は worker のインスタンスではない
- job はあくまでworker の実行指示情報です。worker クラスそのものが job になるわけではない。
- Redis に保存されたjob を Sidekiq が読み込み、対応する worker クラスを呼び出すことでタスクが実行される。
補足 2: Client と Server の両方で worker クラスを実装する必要がある
正確には Client では job を enqueue さえできればよく、上記でいうところの MyWorker#perform("Alice")
が実行されることはないので、その perform
メソッドの実装は空でも問題はない。ただし、job 作成のために worker クラスは必要。ここは Sidekiq を知った当初ピンとこなかった。
正確な用語
Best Practices にきたら、こんなことが書いてある。
Use those terms: job class, thread, process or job. Dump "worker" from your vocabulary.
worker という言葉は使わないで、と。worker を job class と読み替えれば良いのかな。
冪等性とトランザクション
同じく Best Practices から。
Just remember that Sidekiq will execute your job at least once, not exactly once. Even a job which has completed can be re-run. Redis can go down between the point where your job finished but before Sidekiq has acknowledged it in Redis. Sidekiq makes no exactly-once guarantee at all.
Sidekiq は job を正確に 1 回だけ実行するという保証はしていませんとのこと。複数回実行されることもあり得るので、そうなっても問題とならないように job class の実装をしないといけない。大事なことだけど見逃しそう...。
懸念
Client と Server の実行環境を分離する場合、そのふたつの間でどうにかして同じ job class を参照できるようにしないといけない。
- Client: job の enqueue のため。
- Server: job を dequeue して、その job class の
perform
メソッドを実行するため。
それを実現するための案
- コードレプリケーション: Client と Server でコピペにより同じ Job class を持つ。
- 共有ライブラリ: Job class を Gem 化して Client と Server で使う。
- Client をバックグラウンドプロセスのトリガーとなるアプリケーションから分離する。
そもそも Client と Server を分けたいという動機は、アプリケーションとバックグラウンドプロセスを分けることによるリソースの効率化だったり、Server の拡張性の担保だったりするはず。そうであれば分離の境界はアプリケーション + Client | Server
ではなくてアプリケーション | Client + Server
で良いかもしれない。ただそうするとアプリケーションから Client に対して何かしらの方法で job の作成をリクエストできるようにしないといけない。 - Client と Server 両方に同じコードをデプロイする。Server では Sidekiq プロセスも起動する。
どれを選択するかは、それぞれの pros/cons からトレードオフを判断して決めるのが良いかな。
Discussion