README.md in clockwork-0.7.7 vs README.md in clockwork-1.0.0

- old
+ new

@@ -101,55 +101,75 @@ every(1.hour, 'feeds.refresh') { Feed.send_later(:refresh) } every(1.day, 'reminders.send', :at => '01:30') { Reminder.send_later(:send_reminders) } ``` -Use with database tasks +Use with database events ----------------------- -You can dynamically add tasks from a database to be scheduled along with the regular events in clock.rb. +In addition to managing static events in your `clock.rb`, you can configure clockwork to synchronise with dynamic events from a database. Like static events, these database-backed events say when they should be run, and how frequently; the difference being that if you change those settings in the database, they will be reflected in clockwork. -To do this, use the `sync_database_tasks` method call: +To keep the database events in sync with clockwork, a special manager class `DatabaseEvents::Manager` is used. You tell it to sync a database-backed model using the `sync_database_events` method, and then, at the frequency you specify, it will fetch all the events from the database, and ensure clockwork is using the latest settings. +### Example `clock.rb` file + +Here we're using an `ActiveRecord` model called `ClockworkDatabaseEvent` to store events in the database: + ```ruby require 'clockwork' -require 'clockwork/manager_with_database_tasks' +require 'clockwork/manager_with_database_events' require_relative './config/boot' require_relative './config/environment' - + module Clockwork # required to enable database syncing support - Clockwork.manager = ManagerWithDatabaseTasks.new + Clockwork.manager = DatabaseEvents::Manager.new - sync_database_tasks model: MyScheduledTask, every: 1.minute do |instance_job_name| - # Where your model will acts as a worker: - id = instance_job_name.split(':').last - task = MyScheduledTask.find(id) - task.perform_async + sync_database_events model: ClockworkDatabaseEvent, every: 1.minute do |model_instance| - # Or, e.g. if your queue system just needs job names - # Stalker.enqueue(instance_job_name) + # do some work e.g... + + # running a DelayedJob task, where #some_action is a method + # you've defined on the model, which does the work you need + model_instance.delay.some_action + + # performing some work with Sidekiq + YourSidekiqWorkerClass.perform_async end - [...other tasks if you have...] + [other events if you have] end ``` -This tells clockwork to fetch all MyScheduledTask instances from the database, and create an event for each, configured based on the instances' `frequency`, `name`, and `at` methods. It also says to reload the tasks from the database every 1.minute - we need to frequently do this as they could have changed (but you can choose a sensible reload frequency by changing the `every:` option). +This tells clockwork to fetch all `ClockworkDatabaseEvent` instances from the database, creating an internal clockwork event for each one, configured based on the instance's `frequency`, `at` and optionally `name` and `tz` methods. It also says to reload the events from the database every `1.minute`; we need pick up any changes in the database frequently (choose a sensible reload frequency by changing the `every:` option). -Rails ActiveRecord models are a perfect candidate for the model class, but you could use something else. The only requirements are: +When one of the events is ready to be run (based on it's `frequency`, `at` and possible `tz` methods), clockwork arranges for the block passed to `sync_database_events` to be run. The above example shows how you could use either DelayedJob or Sidekiq to simply kick off a worker job. This approach is good because the ideal is to use clockwork as a simple scheduler, and avoid making it carry out any long-running tasks. +### Your Model Classes + +`ActiveRecord` models are a perfect candidate for the model class. Having said that, the only requirements are: + 1. the class responds to `all` returning an array of instances from the database + 2. the instances returned respond to: - `frequency` returning the how frequently (in seconds) the database task should be run - `name` returning the task's job name (this is what gets passed into the block above) - `at` return nil or `''` if not using `:at`, or otherwise any acceptable clockwork `:at` string -Here's an example of one way of setting up your ActiveRecord models, using Sidekiq for background tasks, and making the model class a worker: + - `id` returning a unique identifier (this is needed to track changes to event settings) + - `frequency` returning the how frequently (in seconds) the database event should be run + + - `at` return nil or `''` if not using `:at`, or otherwise any acceptable clockwork `:at` string + + - (optionally) `name` returning the name for the event (used to identify it in the Clcockwork output) + + - (optionally) `tz` returning the timezone to use (default is the local timezone) + +#### Example Setup + +Here's an example of one way of setting up your ActiveRecord models: + ```ruby # db/migrate/20140302220659_create_frequency_periods.rb class CreateFrequencyPeriods < ActiveRecord::Migration def change create_table :frequency_periods do |t| @@ -158,49 +178,34 @@ t.timestamps end end end -# 20140302221102_create_my_scheduled_tasks.rb -class CreateMyScheduledTasks < ActiveRecord::Migration +# 20140302221102_create_clockwork_database_events.rb +class CreateClockworkDatabaseEvents < ActiveRecord::Migration def change - create_table :my_scheduled_tasks do |t| + create_table :clockwork_database_events do |t| t.integer :frequency_quantity t.references :frequency_period t.string :at t.timestamps end - add_index :my_scheduled_tasks, :frequency_period_id + add_index :clockwork_database_events, :frequency_period_id end end -# app/models/my_scheduled_task.rb -class MyScheduledTask < ActiveRecord::Base - include Sidekiq::Worker - +# app/models/clockwork_database_event.rb +class ClockworkDatabaseEvent < ActiveRecord::Base belongs_to :frequency_period - attr_accessible :frequency_quantity, :frequency_period_id, :at + attr_accessible :frequency_quantity, :frequency_period_id, :at - # Used by clockwork to schedule how frequently this task should be run + # Used by clockwork to schedule how frequently this event should be run # Should be the intended number of seconds between executions def frequency frequency_quantity.send(frequency_period.name.pluralize) end - - # Used by clockwork to name this task internally for its logging - # Should return a reference for this task to be used in clockwork - # Include the instance ID if you want to be able to retrieve the - # model instance inside the sync_database_tasks block in clock.rb - def name - "Database_MyScheduledTask:#{id}" - end - - # Method required by Sidekiq - def perform - # the task that will be performed in the background - end end # app/models/frequency_period.rb class FrequencyPeriod < ActiveRecord::Base attr_accessible :name @@ -213,12 +218,10 @@ FrequencyPeriod.create(name: period) end ... ``` -You could, of course, create a separate Sidekiq or DelayedJob worker class under app/workers, and simply use the model referenced by clockwork to trigger that worker to run asynchronously. - Event Parameters ---------- ### :at @@ -295,12 +298,16 @@ Clockwork.every(1.second, 'myjob', :if => lambda { |_| true }) ``` ### :thread -An event with `:thread => true` runs in a different thread. +In default, clockwork runs in single-process, single-thread. +If an event handler takes long time, the main routine of clockwork is blocked until it ends. +Clockwork does not misbehave, but the next event is blocked, and runs when the process is returned to the clockwork routine. +This `:thread` option is to avoid blocking. An event with `:thread => true` runs in a different thread. + ```ruby Clockwork.every(1.day, 'run.me.in.new.thread', :thread => true) ``` If a job is long-running or IO-intensive, this option helps keep the clock precise. @@ -439,15 +446,15 @@ ``` Finally, you can use tasks synchronised from a database as described in detail above: ```ruby -sync_database_tasks model: MyScheduledTask, every: 1.minute do |instance_job_name| +sync_database_events model: MyEvent, every: 1.minute do |instance_job_name| # what to do with each instance end ``` -You can use multiple `sync_database_tasks` if you wish, so long as you use different model classes for each (ActiveRecord Single Table Inheritance could be a good idea if you're doing this). +You can use multiple `sync_database_events` if you wish, so long as you use different model classes for each (ActiveRecord Single Table Inheritance could be a good idea if you're doing this). In production ------------- Only one clock process should ever be running across your whole application