LoginSignup
18
18

More than 5 years have passed since last update.

Sidekiq で queue に入った job を手動で削除する

Last updated at Posted at 2016-01-26

通常必要になることは無いと思いますが、イレギュラーの運用において実行するハメになることがあります :cry:

公式でも 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::QueueEnumerable を実装しているので、

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 を利用しているだけなので速度としては変わりません。

これは、SidekiqQueue は 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 を使っていないか、他の型と組み合わせているのかな。

18
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
18