sskirby-resque-scheduler 1.10.9 → 1.10.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,11 +25,12 @@ module Resque
25
25
 
26
26
  # Schedule all jobs and continually look for delayed jobs (never returns)
27
27
  def run
28
-
28
+ $0 = "resque-scheduler: Starting"
29
29
  # trap signals
30
30
  register_signal_handlers
31
31
 
32
32
  # Load the schedule into rufus
33
+ procline "Loading Schedule"
33
34
  load_schedule!
34
35
 
35
36
  # Now start the scheduling part of the loop.
@@ -68,6 +69,8 @@ module Resque
68
69
  Resque.schedule.each do |name, config|
69
70
  load_schedule_job(name, config)
70
71
  end
72
+ Resque.redis.del(:schedules_changed)
73
+ procline "Schedules Loaded"
71
74
  end
72
75
 
73
76
  # Loads a job schedule into the Rufus::Scheduler and stores it in @@scheduled_jobs
@@ -78,13 +81,24 @@ module Resque
78
81
  # to.
79
82
  if config['rails_env'].nil? || rails_env_matches?(config)
80
83
  log! "Scheduling #{name} "
81
- if !config['cron'].nil? && config['cron'].length > 0
82
- @@scheduled_jobs[name] = rufus_scheduler.cron config['cron'] do
83
- log! "queuing #{config['class']} (#{name})"
84
- enqueue_from_config(config)
84
+ interval_defined = false
85
+ interval_types = %w{cron every}
86
+ interval_types.each do |interval_type|
87
+ if !config[interval_type].nil? && config[interval_type].length > 0
88
+ begin
89
+ @@scheduled_jobs[name] = rufus_scheduler.send(interval_type, config[interval_type]) do
90
+ log! "queueing #{config['class']} (#{name})"
91
+ enqueue_from_config(config)
92
+ end
93
+ rescue Exception => e
94
+ log! "#{e.class.name}: #{e.message}"
95
+ end
96
+ interval_defined = true
97
+ break
85
98
  end
86
- else
87
- log! "no cron found for #{config['class']} (#{name}) - skipping"
99
+ end
100
+ unless interval_defined
101
+ log! "no #{interval_types.join(' / ')} found for #{config['class']} (#{name}) - skipping"
88
102
  end
89
103
  end
90
104
  end
@@ -96,14 +110,14 @@ module Resque
96
110
  end
97
111
 
98
112
  # Handles queueing delayed items
99
- def handle_delayed_items
100
- item = nil
101
- begin
102
- if timestamp = Resque.next_delayed_timestamp
113
+ def handle_delayed_items(at_time = nil)
114
+ if timestamp = Resque.next_delayed_timestamp(at_time)
115
+ procline "Processing Delayed Items"
116
+ while !timestamp.nil?
103
117
  enqueue_delayed_items_for_timestamp(timestamp)
118
+ timestamp = Resque.next_delayed_timestamp(at_time)
104
119
  end
105
- # continue processing until there are no more ready timestamps
106
- end while !timestamp.nil?
120
+ end
107
121
  end
108
122
 
109
123
  # Enqueues all delayed jobs for a timestamp
@@ -164,32 +178,26 @@ module Resque
164
178
  end
165
179
 
166
180
  def reload_schedule!
167
- log! "Reloading Schedule..."
181
+ procline "Reloading Schedule"
168
182
  clear_schedule!
169
183
  Resque.reload_schedule!
170
184
  load_schedule!
171
185
  end
172
186
 
173
187
  def update_schedule
174
- schedule_from_redis = Resque.get_schedules
175
- if !schedule_from_redis.nil? && schedule_from_redis != Resque.schedule
176
- log "Updating schedule..."
177
- # unload schedules that no longer exist
178
- (Resque.schedule.keys - schedule_from_redis.keys).each do |name|
179
- unschedule_job(name)
180
- end
181
-
182
- # find changes and stop and reload or add new
183
- schedule_from_redis.each do |name, config|
184
- if (Resque.schedule[name].nil? || Resque.schedule[name].empty?) || (config != Resque.schedule[name])
185
- unschedule_job(name)
186
- load_schedule_job(name, config)
188
+ if Resque.redis.scard(:schedules_changed) > 0
189
+ procline "Updating schedule"
190
+ Resque.reload_schedule!
191
+ while schedule_name = Resque.redis.spop(:schedules_changed)
192
+ if Resque.schedule.keys.include?(schedule_name)
193
+ unschedule_job(schedule_name)
194
+ load_schedule_job(schedule_name, Resque.schedule[schedule_name])
195
+ else
196
+ unschedule_job(schedule_name)
187
197
  end
188
198
  end
189
-
190
- # load new schedule into Resque.schedule
191
- Resque.schedule = schedule_from_redis
192
199
  end
200
+ procline "Schedules Loaded"
193
201
  end
194
202
 
195
203
  def unschedule_job(name)
@@ -222,6 +230,11 @@ module Resque
222
230
  # add "verbose" logic later
223
231
  log!(msg) if verbose
224
232
  end
233
+
234
+ def procline(string)
235
+ $0 = "resque-scheduler-#{ResqueScheduler::Version}: #{string}"
236
+ log! $0
237
+ end
225
238
 
226
239
  end
227
240
 
@@ -10,7 +10,7 @@
10
10
  <th></th>
11
11
  <th>Name</th>
12
12
  <th>Description</th>
13
- <th>Cron</th>
13
+ <th>Interval</th>
14
14
  <th>Class</th>
15
15
  <th>Queue</th>
16
16
  <th>Arguments</th>
@@ -26,7 +26,9 @@
26
26
  </td>
27
27
  <td><%= h name %></td>
28
28
  <td><%= h config['description'] %></td>
29
- <td style="white-space:nowrap"><%= h config['cron'] %></td>
29
+ <td style="white-space:nowrap"><%= (config['cron'].nil? && !config['every'].nil?) ?
30
+ h('every: ' + config['every']) :
31
+ h('cron: ' + config['cron']) %></td>
30
32
  <td><%= h config['class'] %></td>
31
33
  <td><%= h config['queue'] || queue_from_class_name(config['class']) %></td>
32
34
  <td><%= h config['args'].inspect %></td>
@@ -1,3 +1,3 @@
1
1
  module ResqueScheduler
2
- Version = '1.10.9'
2
+ Version = '1.10.13'
3
3
  end
@@ -18,6 +18,8 @@ module ResqueScheduler
18
18
  #
19
19
  # :name can be anything and is used only to describe the scheduled job
20
20
  # :cron can be any cron scheduling string :job can be any resque job class
21
+ # :every can be used in lieu of :cron. see rufus-scheduler's 'every' usage for
22
+ # valid syntax. If :cron is present it will take precedence over :every.
21
23
  # :class must be a resque worker class
22
24
  # :args can be any yaml which will be converted to a ruby literal and passed
23
25
  # in a params. (optional)
@@ -26,6 +28,11 @@ module ResqueScheduler
26
28
  # an array, each element in the array is passed as a separate param,
27
29
  # otherwise params is passed in as the only parameter to perform.
28
30
  def schedule=(schedule_hash)
31
+ if Resque::Scheduler.dynamic
32
+ schedule_hash.each do |name, job_spec|
33
+ set_schedule(name, job_spec)
34
+ end
35
+ end
29
36
  @schedule = schedule_hash
30
37
  end
31
38
 
@@ -54,7 +61,12 @@ module ResqueScheduler
54
61
 
55
62
  # create or update a schedule with the provided name and configuration
56
63
  def set_schedule(name, config)
57
- redis.hset(:schedules, name, encode(config))
64
+ existing_config = get_schedule(name)
65
+ unless existing_config && existing_config == config
66
+ redis.hset(:schedules, name, encode(config))
67
+ redis.sadd(:schedules_changed, name)
68
+ end
69
+ config
58
70
  end
59
71
 
60
72
  # retrive the schedule configuration for the given name
@@ -65,6 +77,7 @@ module ResqueScheduler
65
77
  # remove a given schedule by name
66
78
  def remove_schedule(name)
67
79
  redis.hdel(:schedules, name)
80
+ redis.sadd(:schedules_changed, name)
68
81
  end
69
82
 
70
83
  # This method is nearly identical to +enqueue+ only it also
@@ -122,8 +135,8 @@ module ResqueScheduler
122
135
 
123
136
  # Returns the next delayed queue timestamp
124
137
  # (don't call directly)
125
- def next_delayed_timestamp
126
- items = redis.zrangebyscore :delayed_queue_schedule, '-inf', Time.now.to_i, :limit => [0, 1]
138
+ def next_delayed_timestamp(at_time=nil)
139
+ items = redis.zrangebyscore :delayed_queue_schedule, '-inf', (at_time || Time.now).to_i, :limit => [0, 1]
127
140
  timestamp = items.nil? ? nil : Array(items).first
128
141
  timestamp.to_i unless timestamp.nil?
129
142
  end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{sskirby-resque-scheduler}
8
- s.version = "1.10.9"
8
+ s.version = "1.10.13"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Ben VandenBos", "Brian Landau", "Sean Kirby", "Tanzeeb Khalili"]
12
- s.date = %q{2010-11-02}
12
+ s.date = %q{2011-08-31}
13
13
  s.description = %q{Light weight job scheduling on top of Resque.
14
14
  Adds methods enqueue_at/enqueue_in to schedule jobs in the future.
15
15
  Also supports queueing jobs on a fixed, cron-like schedule.}
@@ -50,6 +50,16 @@ class Resque::DelayedQueueTest < Test::Unit::TestCase
50
50
  assert_nil(read_timestamp, "No timestamps should be ready for queueing")
51
51
  end
52
52
 
53
+ def test_something_in_the_future_comes_out_if_you_want_it_to
54
+ timestamp = Time.now + 600 # 10 minutes from now
55
+
56
+ Resque.enqueue_at(timestamp, SomeIvarJob, "path")
57
+
58
+ read_timestamp = Resque.next_delayed_timestamp(timestamp)
59
+
60
+ assert_equal(timestamp.to_i, read_timestamp, "The timestamp we pull out of redis should match the one we put in")
61
+ end
62
+
53
63
  def test_enqueue_at_and_enqueue_in_are_equivelent
54
64
  timestamp = Time.now + 60
55
65
 
@@ -120,6 +130,17 @@ class Resque::DelayedQueueTest < Test::Unit::TestCase
120
130
  Resque.expects(:queue_from_class).never # Should NOT need to load the class
121
131
  Resque::Scheduler.handle_delayed_items
122
132
  end
133
+
134
+ def test_handle_delayed_items_with_items_in_the_future
135
+ t = Time.now + 60 # in the future
136
+ Resque.enqueue_at(t, SomeIvarJob)
137
+ Resque.enqueue_at(t, SomeIvarJob)
138
+
139
+ # 2 SomeIvarJob jobs should be created in the "ivar" queue
140
+ Resque::Job.expects(:create).twice.with('ivar', SomeIvarJob, nil)
141
+ Resque.expects(:queue_from_class).never # Should NOT need to load the class
142
+ Resque::Scheduler.handle_delayed_items(t)
143
+ end
123
144
 
124
145
  def test_enqueue_delayed_items_for_timestamp
125
146
  t = Time.now + 60
@@ -7,12 +7,19 @@ class Resque::SchedulerTest < Test::Unit::TestCase
7
7
  end
8
8
 
9
9
  def setup
10
+ Resque::Scheduler.dynamic = false
10
11
  Resque.redis.del(:schedules)
12
+ Resque.redis.del(:schedules_changed)
11
13
  Resque::Scheduler.mute = true
12
14
  Resque::Scheduler.clear_schedule!
13
15
  Resque::Scheduler.send(:class_variable_set, :@@scheduled_jobs, {})
14
16
  end
15
17
 
18
+ def test_enqueue_from_config_with_every_syntax
19
+ Resque::Job.stubs(:create).once.returns(true).with('james_queue', SomeIvarJob, '/tmp')
20
+ Resque::Scheduler.enqueue_from_config('every' => '1m', 'class' => 'SomeIvarJob', 'args' => '/tmp', 'queue' => 'james_queue')
21
+ end
22
+
16
23
  def test_enqueue_from_config_puts_stuff_in_the_resque_queue
17
24
  Resque::Job.stubs(:create).once.returns(true).with(:ivar, SomeIvarJob, '/tmp')
18
25
  Resque::Scheduler.enqueue_from_config('cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp")
@@ -72,10 +79,8 @@ class Resque::SchedulerTest < Test::Unit::TestCase
72
79
  end
73
80
 
74
81
  def test_can_reload_schedule
82
+ Resque::Scheduler.dynamic = true
75
83
  Resque.schedule = {"some_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"}}
76
- Resque.redis.hset(:schedules, "some_ivar_job", Resque.encode(
77
- {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"}
78
- ))
79
84
 
80
85
  Resque::Scheduler.load_schedule!
81
86
 
@@ -120,6 +125,7 @@ class Resque::SchedulerTest < Test::Unit::TestCase
120
125
  end
121
126
 
122
127
  def test_update_schedule
128
+ Resque::Scheduler.dynamic = true
123
129
  Resque.schedule = {
124
130
  "some_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"},
125
131
  "another_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/5"},
@@ -128,15 +134,16 @@ class Resque::SchedulerTest < Test::Unit::TestCase
128
134
 
129
135
  Resque::Scheduler.load_schedule!
130
136
 
131
- Resque.redis.hset(:schedules, "some_ivar_job", Resque.encode(
137
+ Resque.set_schedule("some_ivar_job",
132
138
  {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2"}
133
- ))
134
- Resque.redis.hset(:schedules, "new_ivar_job", Resque.encode(
139
+ )
140
+ Resque.set_schedule("new_ivar_job",
135
141
  {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp/3"}
136
- ))
137
- Resque.redis.hset(:schedules, "stay_put_job", Resque.encode(
142
+ )
143
+ Resque.set_schedule("stay_put_job",
138
144
  {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp"}
139
- ))
145
+ )
146
+ Resque.remove_schedule("another_ivar_job")
140
147
 
141
148
  Resque::Scheduler.update_schedule
142
149
 
@@ -148,9 +155,11 @@ class Resque::SchedulerTest < Test::Unit::TestCase
148
155
  end
149
156
  assert !Resque::Scheduler.scheduled_jobs.keys.include?("another_ivar_job")
150
157
  assert !Resque.schedule.keys.include?("another_ivar_job")
158
+ assert_equal 0, Resque.redis.scard(:schedules_changed)
151
159
  end
152
160
 
153
161
  def test_update_schedule_with_mocks
162
+ Resque::Scheduler.dynamic = true
154
163
  Resque.schedule = {
155
164
  "some_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"},
156
165
  "another_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/5"},
@@ -162,15 +171,16 @@ class Resque::SchedulerTest < Test::Unit::TestCase
162
171
  Resque::Scheduler.rufus_scheduler.expects(:unschedule).with(Resque::Scheduler.scheduled_jobs["some_ivar_job"].job_id)
163
172
  Resque::Scheduler.rufus_scheduler.expects(:unschedule).with(Resque::Scheduler.scheduled_jobs["another_ivar_job"].job_id)
164
173
 
165
- Resque.redis.hset(:schedules, "some_ivar_job", Resque.encode(
174
+ Resque.set_schedule("some_ivar_job",
166
175
  {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2"}
167
- ))
168
- Resque.redis.hset(:schedules, "new_ivar_job", Resque.encode(
176
+ )
177
+ Resque.set_schedule("new_ivar_job",
169
178
  {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp/3"}
170
- ))
171
- Resque.redis.hset(:schedules, "stay_put_job", Resque.encode(
179
+ )
180
+ Resque.set_schedule("stay_put_job",
172
181
  {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp"}
173
- ))
182
+ )
183
+ Resque.remove_schedule("another_ivar_job")
174
184
 
175
185
  Resque::Scheduler.update_schedule
176
186
 
@@ -181,6 +191,16 @@ class Resque::SchedulerTest < Test::Unit::TestCase
181
191
  end
182
192
  assert !Resque::Scheduler.scheduled_jobs.keys.include?("another_ivar_job")
183
193
  assert !Resque.schedule.keys.include?("another_ivar_job")
194
+ assert_equal 0, Resque.redis.scard(:schedules_changed)
195
+ end
196
+
197
+ def test_set_schedules
198
+ Resque::Scheduler.dynamic = true
199
+ Resque.schedule = {"my_ivar_job" => {
200
+ 'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/75"
201
+ }}
202
+ assert_equal({'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/75"},
203
+ Resque.decode(Resque.redis.hget(:schedules, "my_ivar_job")))
184
204
  end
185
205
 
186
206
  def test_set_schedule
@@ -189,6 +209,7 @@ class Resque::SchedulerTest < Test::Unit::TestCase
189
209
  })
190
210
  assert_equal({'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/22"},
191
211
  Resque.decode(Resque.redis.hget(:schedules, "some_ivar_job")))
212
+ assert Resque.redis.sismember(:schedules_changed, "some_ivar_job")
192
213
  end
193
214
 
194
215
  def test_get_schedule
@@ -205,11 +226,13 @@ class Resque::SchedulerTest < Test::Unit::TestCase
205
226
  ))
206
227
  Resque.remove_schedule("some_ivar_job3")
207
228
  assert_equal nil, Resque.redis.hget(:schedules, "some_ivar_job3")
229
+ assert Resque.redis.sismember(:schedules_changed, "some_ivar_job3")
208
230
  end
209
231
 
210
232
  def test_adheres_to_lint
211
233
  assert_nothing_raised do
212
234
  Resque::Plugin.lint(Resque::Scheduler)
235
+ Resque::Plugin.lint(ResqueScheduler)
213
236
  end
214
237
  end
215
238
 
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 1
7
7
  - 10
8
- - 9
9
- version: 1.10.9
8
+ - 13
9
+ version: 1.10.13
10
10
  platform: ruby
11
11
  authors:
12
12
  - Ben VandenBos
@@ -17,7 +17,7 @@ autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
19
 
20
- date: 2010-11-02 00:00:00 -04:00
20
+ date: 2011-08-31 00:00:00 -04:00
21
21
  default_executable:
22
22
  dependencies:
23
23
  - !ruby/object:Gem::Dependency