9

i'm trying to get the run_at datetime in a custom job class. my code looks like this:

class MyCustomJob < Struct.new(:my_object)

  def perform
    if self.run_at == my_object.start_time
      # process the job
    end
  end

end

i also tried a Delayed::Job.find(self) but had no luck.

thanks in advance

klochner
  • 8,077
  • 1
  • 33
  • 45
Oliver
  • 801
  • 13
  • 26

2 Answers2

10

If you define a before method on your custom job, the worker will pass you the delayed job instance before calling perform:

class MyCustomTask

  def before(job)
    @job = job
  end

  def perform
    # has access to @job object. 
    # You may need to call @job.reload to see in-flight changes to object in the database.
  end
end
nicsnoek
  • 109
  • 1
  • 2
  • Argh, sorry. Downvoted by mistake and now I cannot undo it unless the answer is edited :-( This answer was the most useful for us and the one that actually chose. Thanks. – Vicente Quintans Sep 11 '20 at 14:26
8

You should handle this when you create the job:

   priority = 0
   run_time = my_object.start_time
   Delayed::Job.enqueue(MyCustomJob.new(my_object), priority, run_time)

https://github.com/tobi/delayed_job/wiki

If your jobs aren't running at the expected time, you may be scheduling them for UTC:

http://www.gregbenedict.com/2009/08/19/is-delayed-job-run_at-datetime-giving-you-fits/

To check the queue for an existing job - you could do the following:


class MyCustomJob < Struct.new(:object_id)

  def self.exists?(object_id)
    Delayed::Job.where(['handler = ? and failed_at is null',handler(object_id)]).count(:all) > 0
  end

  def self.handler(object_id)
    "--- !ruby/struct:MyCustomJob \nobject_id: #{object_id}\n"
  end

  def perform
    my_object = MyObject.find(object_id)
    my_object.do_stuff
  end
end

Then just check for MyCustomJob.exists?(my_object.id) before queueing.

It's a little bit of a hack - edit the handler method as needed. I would modify the delayed_jobs table to have a class/object_id to make for cleaner code and more efficient table scans if your jobs table is large or if you do this with other types of jobs.

This question also looks relevant:

How to cancel scheduled job with delayed_job in Rails?

Community
  • 1
  • 1
klochner
  • 8,077
  • 1
  • 33
  • 45
  • hm i already queue my job like this. i need to check if start_time changed on object when the job runs. – Oliver Jul 27 '11 at 20:48
  • thanks for your reply! this is no timing problem.. lets say i've an event in a calendar.. i would like to send a reminder 10minutes before the event starts.. so theres a job in the queue for that.. someone changes the event to the next day.. so i need to check if the current job is still valid.. – Oliver Jul 27 '11 at 21:17
  • can't you just check whether object.start_time > Time.now? – klochner Jul 28 '11 at 04:13
  • yeah but i'm queuing the jobs in an after_save hook. so lets say the event gets postponed to the day after.. then the event gets shifted one day back again.. then both jobs get fired.. or am i wrong – Oliver Jul 28 '11 at 08:09
  • 1
    I have a similar case - before I queue a job I search the table for any existing jobs. I'll post some code that might be helpful. – klochner Jul 28 '11 at 15:13
  • Is there a special reason for including "and failed_at is null"? Wouldn't it lead to task duplication in case task A failed and waiting for retry and we check if task B should be launched? – Andrey Sereda Jan 16 '14 at 12:44
  • 1
    failed_at only gets set when all retries have failed -- "The default Worker.max_attempts is 25. After this, the job either deleted (default), or left in the database with "failed_at" set." – klochner Jan 20 '14 at 19:39
  • I know that the post is already pretty old, but it seems somewhat weird that I can't get the `run_at` attribute from within a job but instead have to pass it as an argument when queuing the job. Is it really the most optimised way? – Kkulikovskis Jan 13 '16 at 13:40