如何在Sidekiq中删除一个任务

58

我在我的Rails应用程序中使用Sidekiq。我的应用程序用户创建报告,这些报告会启动Sidekiq作业。然而,有时用户希望能够取消“处理”报告。删除报告很容易,但我还需要能够删除Sidekiq作业。

到目前为止,我已经成功获取了工作者列表:

workers = Sidekiq::Workers.new

每个工作人员都有参数,包括报告 ID,以便我可以确定哪个工作属于哪个报告。但是,我不确定如何实际删除该工作。需要注意的是,无论工作当前是否繁忙或设置为重试,我都想删除该工作。

8个回答

108
根据这个Sidekiq文档页面,要删除一个具有唯一id的作业,您需要遍历队列并在其上调用.delete
queue = Sidekiq::Queue.new("mailer")
queue.each do |job|
  job.klass # => 'MyWorker'
  job.args # => [1, 2, 3]
  job.delete if job.jid == 'abcdef1234567890'
end

还有一个名为sidekiq-status的插件,它使您能够取消单个作业。

scheduled_job_id = MyJob.perform_in 3600
Sidekiq::Status.cancel scheduled_job_id #=> true

1
对于你的第一段代码,那不是在删除队列中的作业吗?如果它已经处于繁忙状态或正在重试,会怎样呢? - Matthew Berman
1
据我所见,在这两种情况下,都没有办法终止已经在运行的作业。 - Simone Carletti
嗯...那似乎相当不直观,对吧?我只能接受作业一直运行直到完成,即使我需要停止它? - Matthew Berman
1
现在就是这样。终止正在运行的作业需要存储工作人员并向其发送信号,但还需要监督者。这并不是一件简单的事情。 - Simone Carletti
看起来上面的代码可能会进行分页,因此如果您有一个特别大的作业集(例如> 5000),您可能需要多次运行。 - Solomons_Ecclesiastes
@Solomons_Ecclesiastes 在暂停队列后尝试运行代码。根据我的经验,这样做可以在不中断执行的情况下正常工作。 - M. Habib

31

我发现最简单的方法是:

job = Sidekiq::ScheduledSet.new.find_job([job_id])

其中 [job_id] 是与报告相关的 JID。

job.delete

我发现没有必要像其他答案中描述的那样遍历整个队列。


5
find_job使用相同的线性搜索。这是来自文档的评论:"如果您的队列很大,这非常低效!"。 - bartolo-otrit
4
@bartolo-otrit,你建议用什么代替? - pierrea
通过JID查找工作(警告:如果您的队列很大,这非常低效!) - Vijay Chouhan

20

我有同样的问题,不过我的区别在于我需要取消一个预定的任务,我的解决方案是:

Sidekiq::ScheduledSet.new.each do |_job|
  next unless [online_jid, offline_jid].include? _job.jid
  status = _job.delete
end

1
create_log 是用来做什么的? - thekingoftruth

7
如果您想取消一个计划任务,我不确定@KimiGao的回答是否正确,但这是我从Sidekiq当前的API文档中适应的:
jid = MyCustomWorker.perform_async

r = Sidekiq::ScheduledSet.new
jobs = r.select{|job| job.jid == jid }
jobs.each(&:delete)

希望它有所帮助。

7
您可以通过工作类和参数来删除Sidekiq作业过滤:
class UserReportsWorker
  include Sidekiq::Worker
  def perform(report_id)
    # ...
  end
end


jobs = Sidekiq::ScheduledSet.new.select do |retri| 
  retri.klass == "UserReportsWorker" && retri.args == [42]
end
jobs.each(&:delete)

3

我曾经遇到过同样的问题。 我通过在初始化时注册作业ID并创建另一个名为cancel!的函数来解决它。

以下是代码:

after_enqueue do |job|
  sidekiq_job = nil

  queue = Sidekiq::Queue.new
  sidekiq_job = queue.detect do |j|
    j.item['args'][0]['job_id'] == job.job_id
  end

  if sidekiq_job.nil?
    scheduled = Sidekiq::ScheduledSet.new
    sidekiq_job = scheduled.detect do |j|
      j.item['args'][0]['job_id'] == job.job_id
    end
  end

  if sidekiq_job.present?
    booking = job.arguments.first
    booking.close_comments_jid = sidekiq_job.jid
    booking.save
  end
end


def perform(booking)
   # do something
end

def self.cancel!(booking)
  queue = Sidekiq::Queue.new
  sidekiq_job = queue.find_job(booking.close_comments_jid)

  if sidekiq_job.nil?
    scheduled = Sidekiq::ScheduledSet.new
    sidekiq_job = scheduled.find_job(booking.close_comments_jid)
  end

  if sidekiq_job.nil?
    # Report bug in my Bug Tracking tool
  else
    sidekiq_job.delete

  end
end

2

如果您知道作业的 job_id,那么有一种简单的方法可以删除它:

job = Sidekiq::ScheduledSet.new.find_job(job_id)
begin
  job.delete
rescue
  Rails.logger.error "Job: (job_id: #{job_id}) not found while deleting jobs."
end

2
您可以在Rails服务器上使用Sidekiq页面。
例如,http://localhost:3000/sidekiq,您可以停止/删除Sidekiq作业。
在此之前,您需要更新routes.rb文件。
require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq'

3
需要注意的是,这仅适用于仍在队列中或已计划的作业。对于正在繁忙运行的作业,这不会改变任何事情。 - NemyaNation

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接