scheduling a job every minute Rails 3.1 on Heroku
Asked Answered
T

3

9

I want to run a task every minute on Heroku to check if conditions are met to time-out certain user tasks. I can only run a Heroku cron job every hour, so what's the best way to set up a timed task like this. I am using Rails 3.1 on Heroku.

Ticon answered 6/9, 2011 at 6:56 Comment(0)
H
7

You could use delayed_job with a self-restarting job with a :run_at. Something sort of like this:

class YourJob
    def do_interesting_things
        #... Do what needs to be done.
        self.delay(:run_at => 1.minute.from_now).do_interesting_things
    end

    def self.start_me_up
        new.do_interesting_things
    end
end

And then somewhere during your application's initialization:

YourJob.start_me_up
Honeycomb answered 6/9, 2011 at 7:24 Comment(13)
thank you - i will try that today and if it works i'll accept the answer :-)Ticon
Did this work out? I'm needing to run a rake task every 1 minute and I'm thinking that there should be a way to make it run itself vs requiring cron. Presently cron on heroku has a max of every hour.Bioenergetics
@ylluminate: I never heard back but it should work, try it and let me know how it goes.Honeycomb
You should probably wrap the whole "do what needs to be done" in a big begin,rescue block or else your job won't restart if anything ever goes wrong.Highpitched
@muistooshort i did this, but it looks like one of the workers is constantly running. i guess that is how sidekiq/delayedjob implements it?Misusage
@mingyeow: DJ usually has a rake jobs:work running in the background all the time, that's the process that looks for newly enqueued delayed jobs and runs them.Honeycomb
won't this recursive function lead to a stack overflow?Stanwood
@mhz: The method isn't really recursive. The delay call adds an entry to the DelayedJob queue and DJ will call do_interesting_things when it scans the queue. So do_interesting_things isn't really calling itself, it is asking DJ to call itself later.Honeycomb
@muistooshort it doesn't work? I get an error of "uninitialized constant YourJob" should the method be defined def self.do_interesting_things?Stanwood
@mhz: What is your class called? Presumably something more sensible than YourJob.Honeycomb
Its called VrEmailJob... though not sure why that would be a problem....? Its inheriting from ActiveJob::BaseStanwood
@Stanwood Then why are you using YourJob? This answer is about delayed-job, you'll have to adapt it a little to use it with ActiveJob and you'll have to use the right class names.Honeycomb
@muistooshort I only said YourJob in the comment to put it in the context of this question... I'm not actually using YourJob in my code. I also spent too many hours trying to make delayed_jobs work, and now the jobs are disappearing mysteriously from my database. I'm going to throw in the towel here and try another solution, but thanks anyway.Stanwood
N
9

As of today, you can the scheduling gem, clockwork. Heroku officially supports this and you can find their documentation here.

Nonparous answered 20/6, 2012 at 13:51 Comment(2)
Just note that clockwork requires a dyno, so it will cost the same as any other type of dyno ($0.05 per hour, billable to the second -- about $35/month). Those who don't require scheduling "every minute", but are fine with every 10 minutes, every hour, or every day (at a specific time), then consider the Heroku Scheduler, a free add-on.Unharness
Another limitation of clockwork is that it can only run on a single dyno, so if you have more scheduled work than a single process can handle, the delayed_job solution will work better.Highpitched
H
7

You could use delayed_job with a self-restarting job with a :run_at. Something sort of like this:

class YourJob
    def do_interesting_things
        #... Do what needs to be done.
        self.delay(:run_at => 1.minute.from_now).do_interesting_things
    end

    def self.start_me_up
        new.do_interesting_things
    end
end

And then somewhere during your application's initialization:

YourJob.start_me_up
Honeycomb answered 6/9, 2011 at 7:24 Comment(13)
thank you - i will try that today and if it works i'll accept the answer :-)Ticon
Did this work out? I'm needing to run a rake task every 1 minute and I'm thinking that there should be a way to make it run itself vs requiring cron. Presently cron on heroku has a max of every hour.Bioenergetics
@ylluminate: I never heard back but it should work, try it and let me know how it goes.Honeycomb
You should probably wrap the whole "do what needs to be done" in a big begin,rescue block or else your job won't restart if anything ever goes wrong.Highpitched
@muistooshort i did this, but it looks like one of the workers is constantly running. i guess that is how sidekiq/delayedjob implements it?Misusage
@mingyeow: DJ usually has a rake jobs:work running in the background all the time, that's the process that looks for newly enqueued delayed jobs and runs them.Honeycomb
won't this recursive function lead to a stack overflow?Stanwood
@mhz: The method isn't really recursive. The delay call adds an entry to the DelayedJob queue and DJ will call do_interesting_things when it scans the queue. So do_interesting_things isn't really calling itself, it is asking DJ to call itself later.Honeycomb
@muistooshort it doesn't work? I get an error of "uninitialized constant YourJob" should the method be defined def self.do_interesting_things?Stanwood
@mhz: What is your class called? Presumably something more sensible than YourJob.Honeycomb
Its called VrEmailJob... though not sure why that would be a problem....? Its inheriting from ActiveJob::BaseStanwood
@Stanwood Then why are you using YourJob? This answer is about delayed-job, you'll have to adapt it a little to use it with ActiveJob and you'll have to use the right class names.Honeycomb
@muistooshort I only said YourJob in the comment to put it in the context of this question... I'm not actually using YourJob in my code. I also spent too many hours trying to make delayed_jobs work, and now the jobs are disappearing mysteriously from my database. I'm going to throw in the towel here and try another solution, but thanks anyway.Stanwood
S
1

Clockwork is the way to go but I will suggest another workaround.

Use scheduler (every 10min) + delayed jobs to execute the task every minute. This seems a little bit more reliable solution than the one without scheduler.

MyJob.run!

(1..8).each |i|
   MyJob.delay(run_at: i.minutes.from_now).run! # create 8 more jobs (each start 1 minute later)
end
Submerge answered 20/11, 2020 at 13:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.