sskirby-resque-scheduler 1.10.9 → 1.10.13

Sign up to get free protection for your applications and to get access to all the features.
@@ -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