Skip to content
treeder edited this page Mar 22, 2012 · 1 revision

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!

Other examples

# 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'

Arguments

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.

Timezones

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.

Getting List of Scheduled Jobs

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

Canceling a Scheduled Job

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}

Cautionary Note

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.

Number of Scheduled Jobs

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.

Scheduling Patterns

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

Extra Credit

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.

Note

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.