通常必要になることは無いと思いますが、イレギュラーの運用において実行するハメになることがあります
公式でも https://github.com/mperham/sidekiq/wiki/API#queue ここにしっかり書いてあります。
前提
説明をしやすくするために、今回は以下のように Sidekiq の Worker を呼び出すときに、1つの ID のみを渡していることを想定します。
SampleWorker.perform_async(123456789)
また、queue はデフォルトを利用するという前提です。
class SampleWorker
include Sidekiq::Worker
# 以下のように queue を default 以外を利用する場合は以下の説明もそれに読み替えてください。
# sidekiq_options queue: :sample_worker
def perform(id)
# id 使って DB からデータ読み出したりして処理する想定
end
end
削除方法
# 削除したい id を Worker
deletion_target_id = '12345678'
worker = 'SampleWorker'
queue = Sidekiq::Queue.new('default') # default 以外なら queue 名を指定
queue.each do |job|
next unless job.klass == worker # job['class'] でも同じ
next unless job.args.first == deletion_target_id # args は peform_async に引数が配列で入っている
job.delete
end
Sidekiq::Queue
は Enumerable
を実装しているので、
queue.find {|job| ... }.delete
でも十分です。
もしも jid を保持しているなら
Sidekiq は全ての job に jid
という id をふっています。
> SampleWorker.perform_async(1)
=> "66ede9dfdd7a2d668ccd501e"
現在は item['jid'.freeze] ||= SecureRandom.hex(12)
のような実装なので、特に引数には依存せず生成されます。
https://github.com/mperham/sidekiq/blob/master/lib/sidekiq/client.rb#L214
もしこれを保持しているならば、以下で済ませられます。
Sidekiq::Queue.new('default').find_job(jid)
ただ、内部的には Enumerable#detect
を利用しているだけなので速度としては変わりません。
これは、Sidekiq
の Queue
は Redis の Lists が使われているのですが (perform_in
の場合は Sorted Sets) 、引数も jid もまとめて Lists の値として入っているので、jid
で取り出すからといって有利になる訳ではないからです。
Redis を monitor していると以下のようなコマンドが流れるのが観測できます。
"lpush" "queue:default" "{\"class\":\"SampleWorker\",\"args\":[2],\"retry\":5,\"queue\":\"default\",\"jid\":\"66ede9dfdd7a2d668ccd501e\",\"created_at\":1453850411.437477,\"enqueued_at\":1453850411.437571}"
注意点
ドキュメントにも書かれていますが
Find a job by JID (WARNING: this is very inefficient if your queue is big!)
queue のサイズが大きくなると jid での検索であろうがなかろうがとても遅いです。each のたびに Redis に問い合わせ、Sidekiq::Job
オブジェクトに変換して返しているためだと思います。
each の実装はこの辺です。
https://github.com/mperham/sidekiq/blob/master/lib/sidekiq/api.rb#L230-L249
(Sidekiq 自体) / (Sidekiq への登録処理)を停止して削除を行うべきか?
perform_async
の場合は Redis の Lists を利用し、Sidekiq が fetch する際もブロッキング pop (brpop) を利用するため、仮に Sidekiq が動作していたとしても削除自体は安全に行われることになると思いますが、each
の実装を見てもらってわかる通りで、ページングしつつ range で取得しているだけなので、Lists のサイズがどんどん変わっていくと、期待通りのチェックができなさそうです。
「数件削除するだけ」程度なら再実行も楽なのでいいですが、大量に削除するような場合には、Sidekiq 関連の処理は停止してから行う方が望ましいように感じます。
Pro API を使う
Pro をお使いの場合は以下の API が Lua ベースで提供されており、高速なようですね。
Sidekiq::Queue#delete_job
Sidekiq::Queue#delete_by_class
Sidekiq::JobSet#find_job(jid)
ということは、Pro 版は Lists を使っていないか、他の型と組み合わせているのかな。