-
Notifications
You must be signed in to change notification settings - Fork 13
Schedule Tasks
One of the most useful applications of IronWorker is to replace Cron with a dependable "Cloud Scheduler."
To do this you use the schedule method.
Let's say we have a ReportWorker and instead of queuing it for one-time immediate delivery, you want to schedule it for recurring delivery:
Create a file called schedule_report_worker.rb
and add this:
# needed for time operations
require 'date'
require 'active_support/core_ext'
...
worker = ReportWorker.new
worker.user_id = @current_user.id
worker.schedule(:start_at => Time.now, :run_every => 3600)
Run it and that's it. Your fearless user will get this report in an hour, every hour!
# to run at a higher priority
worker = DataCrunchWorker.new
worker.schedule(:start_at => 15.minutes.since, :priority=>1)
# to schedule a job to recur every day
worker = DailySomethingWorker.new
worker.schedule(:start_at=>1.days.from_now.change(:hour=>3), :run_every=>24*60*60)
# to schedule a job to run every hour for 12 cycles.
worker = DailySomethingWorker.new
worker.schedule(:start_at=>1.minutes.since, :run_every=>60*60, :run_times=>12, :priority=>2)
Note: For many time-based syntaxes like 15.minutes.since and other flexible scheduling methods, you'll want to include the active_support core extensions.
# includes only what's needed
require 'active_support/core_ext'
Scheduling arguments can include:
Required:
- start_at: — Time of first run. (Should be a Time or DateTime object.)
Optional:
- run_every: — Time in seconds between runs. If omitted, task will only run once.
- end_at: — Time tasks will stop being enqueued. If omitted, task will run indefinitely.
- run_times: — Number of times to run task. For example, if run_times: is 5, the task will run 5 times. If omitted, task will run indefinitely.
- priority: — Priority queue to run the job in (0, 1, 2). p0 is default. Run at higher priorities to reduce time jobs may spend in the queue once they come off schedule.
IronWorker is timezone independent, operating off UTC. Just use the timezone that you're comfortable with and it will be maintained at the equivalent of that time/zone within IronWorker. Although working with UTC does tend to make distributed development much easier and clearer.
You can get the list of scheduled jobs by using an IronWorker service call. This will return a list of schedules.
schedules = IronWorker.service.schedules
If you have a number of schedules, you may need to cycle through the pages to process the full list.
page = 0
while true
schedules = IronWorker.service.schedules(:page=>page)['schedules']
page += 1
if schedules.size == 0
return
end
schedules.each do |s|
# do something
end
end
You can cancel a scheduled job either programmatically or through the Scheduled Jobs tab in the HUD.
To cancel it programmatically, just get the scheduled_id and call the IronWorker service API.
IronWorker.service.cancel_schedule(worker.schedule_id)
The response will be deleted which will also show up for the job in the HUD.
{"msg"=>"Deleted", "status_code"=>200}
If run_every is included and end_at and run_times are both omitted, then the jobs will run indefinitely until the scheduled job is cancelled.
IronWorker limits initial accounts to 100 scheduled jobs per project on basic accounts. (Note this is just for scheduled jobs only. Queued jobs are unlimited.)
If you're using good worker patterns, you should be well under the limit (see below for tips). Although if you do need more, please contact us at [email protected] and we can up the limit.
Managing lots of scheduled job within an application can create administrative issues.
For this reason, we recommend adopting certain design patterns around Scheduled Jobs. One in particular is a master/slave pattern whereby you have one or more master scheduled jobs come off schedule and create and queue worker jobs to perform the actually work (ideally with each worker job performing a number of individual tasks so as to amortize the worker setup steps). This is a far better approach than maintaining scheduled jobs for each user or task.
Here's an example where you might have one "master" run each day to do something for each user. The only thing that master worker would do is go through your user table and queue up workers to perform each user task.
Some pseudo code for this "master" worker might look like this:
class DailyUserWorker < IronWorker::Base
merge_worker "task_worker", "TaskWorker"
def run
users = User.all
users.each do |u|
task = TaskWorker.new
task.users_id = u.id
task.queue
end
end
end
The TaskWorker would be the one that performs a task for each user or client. (If the processing time for each task is in seconds, you might want to bundle several users/clients together to amortize worker setup.)
You can see a worker calling worker in the "batch" example in the IronWorker examples on Github.
You can also read more on this pattern (and the corresponding anti-pattern) here:
Pattern: Creating Task-Level Workers at Runtime
Anti-Pattern: Lots of scheduled jobs
One tenet of Cloud Computing is the idea of Disposable Infrastructure. In short, this means architecting for servers to fail.
Building schedulers bring in their own set of problems. If you're running Cron jobs on servers running locally, what process is monitoring them and what happens if the server fails? This is where IronWorker comes in. Running scheduled jobs in the cloud lets you monitor them and verify they're performing correctly. And you never have to worry about it going down again.
When scheduling jobs in IronWorker, you want to do so either externally from your application code or with tests to see if jobs are already running. Otherwise, you may schedule a duplicate job every time your application starts up.