79

Delayed::Job's auto-retry feature is great, but there's a job that I want to manually retry now. Is there a method I can call on the job itself like...

Delayed::Job.all[0].perform

or run, or something. I tried a few things, and combed the documentation, but couldn't figure out how to execute a manual retry of a job.

Michael Waxman
  • 1,815
  • 3
  • 18
  • 32

9 Answers9

115

To manually call a job

Delayed::Job.find(10).invoke_job # 10 is the job.id

This does not remove the job if it is run successfully. You need to remove it manually:

Delayed::Job.find(10).destroy
The Who
  • 6,552
  • 5
  • 36
  • 33
  • 10
    The alternative suggested by @joe is a safer better, especially if the job needs to know whether it is running in script/console or inside a job runner. Try this to queue the job for immediate retry Delayed::Job.first.update_attributes(:attempts=>0, :run_at=>Time.now, :failed_at => nil, :locked_by=>nil, :locked_at=>nil) – so_mv Mar 10 '12 at 00:16
  • 8
    `attempts` cannot be assigned in `update_attributes` because it is a protected attribute. I just do: `dj = Delayed::Job.first; dj.run_at = Time.now; dj.attempts = 0; dj.save!;` – Anjan Aug 20 '12 at 11:00
  • To do it en-masse this worked (approx 100 jobs) Delayed::Job.where.all.each {|dj| dj.run_at = Time.now; dj.attempts = 0; dj.save!} – tobinharris Mar 24 '14 at 13:22
  • 1
    I just do `Delayed::Job.where.not(last_error: nil).update_all(run_at: Time.current)` – Jason Galuten May 16 '18 at 20:34
  • this doesn't set the `last_error` attribute if the job fails – localhostdotdev Apr 18 '19 at 19:01
40
Delayed::Worker.new.run(Delayed::Job.last)

This will remove the job after it is done.

Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
Beena Shetty
  • 3,676
  • 2
  • 28
  • 31
12

You can do it exactly the way you said, by finding the job and running perform.

However, what I generally do is just set the run_at back so the job processor picks it up again.

Joe Martinez
  • 854
  • 4
  • 9
  • 1
    There is no `perform` method for delayed job object. The closest is `Delayed::Job.find(10).payload_object.perform`, and one should not use that. – lulalala Jun 14 '12 at 10:05
10

I have a method in a controller for testing purposes that just resets all delayed jobs when I hit a URL. Not super elegant but works great for me:

# For testing purposes
  def reset_all_jobs
    Delayed::Job.all.each do |dj|
      dj.run_at = Time.now - 1.day
      dj.locked_at = nil
      dj.locked_by = nil
      dj.attempts = 0
      dj.last_error = nil
      dj.save
    end
    head :ok
  end
Tony
  • 18,776
  • 31
  • 129
  • 193
8

Prior answers above might be out of date. I found I needed to set failed_at, locked_by, and locked_at to nil:

(for each job you want to retry):

d.last_error = nil
d.run_at = Time.now
d.failed_at = nil
d.locked_at = nil
d.locked_by = nil
d.attempts = 0
d.failed_at = nil # needed in Rails 5 / delayed_job (4.1.2)
d.save!
David Silva Smith
  • 11,498
  • 11
  • 67
  • 91
jpw
  • 18,697
  • 25
  • 111
  • 187
5

if you have failed delayed job which you need to re-run, then you will need to only select them and set everything refer to failed retry to null:

Delayed::Job.where("last_error is not null").each do |dj|
  dj.run_at = Time.now.advance(seconds: 5)
  dj.locked_at = nil
  dj.locked_by = nil
  dj.attempts = 0
  dj.last_error = nil
  dj.failed_at = nil
  dj.save  
end
mohamed-ibrahim
  • 10,837
  • 4
  • 39
  • 51
4

In a development environment, through rails console, following Joe Martinez's suggestion, a good way to retry all your delayed jobs is:

Delayed::Job.all.each{|d| d.run_at = Time.now; d.save!}
Darme
  • 6,984
  • 5
  • 37
  • 52
  • 6
    Updating `run_at` in 4.0.1 does not appear to be enough. I had to do the following: `Delayed::Job.where("failed_at is not null").each do |dj| dj.run_at = Time.now; dj.last_error = nil; dj.failed_at = nil; dj.save! end` – steakchaser May 13 '14 at 18:43
3
Delayed::Job.all.each(&:invoke_job)
rubycop
  • 31
  • 1
2

Put this in an initializer file!

module Delayed
  module Backend
    module ActiveRecord
      class Job
        def retry!
          self.run_at = Time.now - 1.day
          self.locked_at = nil
          self.locked_by = nil
          self.attempts = 0
          self.last_error = nil
          self.failed_at = nil
          self.save!
        end
      end
    end
  end
end

Then you can run Delayed::Job.find(1234).retry!

This will basically stick the job back into the queue and process it normally.

Blair Anderson
  • 19,463
  • 8
  • 77
  • 114
  • 1
    Yeah, this is a clean way of putting it back in the queue to be processed and letting the workers process it normally. I would only suggest not having `Time.now - 1.day` and just using `Time.now` or `Time.zone.now`. No sense in putting it back a day (except maybe for priority) and it can add confusion if looking at this later on. – Joshua Pinter Dec 07 '21 at 17:46