brianjlandau-resque-scheduler 1.10.11 → 1.10.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{brianjlandau-resque-scheduler}
8
- s.version = "1.10.11"
8
+ s.version = "1.10.12"
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"]
12
- s.date = %q{2010-11-06}
12
+ s.date = %q{2010-11-11}
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.}
@@ -69,6 +69,7 @@ module Resque
69
69
  Resque.schedule.each do |name, config|
70
70
  load_schedule_job(name, config)
71
71
  end
72
+ Resque.redis.del(:schedules_changed)
72
73
  procline "Schedules Loaded"
73
74
  end
74
75
 
@@ -80,17 +81,24 @@ module Resque
80
81
  # to.
81
82
  if config['rails_env'].nil? || rails_env_matches?(config)
82
83
  log! "Scheduling #{name} "
83
- if !config['cron'].nil? && config['cron'].length > 0
84
- begin
85
- @@scheduled_jobs[name] = rufus_scheduler.cron config['cron'] do
86
- log! "queuing #{config['class']} (#{name})"
87
- 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}"
88
95
  end
89
- rescue Exception => e
90
- log! "#{e.class.name}: #{e.message}"
96
+ interval_defined = true
97
+ break
91
98
  end
92
- else
93
- 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"
94
102
  end
95
103
  end
96
104
  end
@@ -102,13 +110,12 @@ module Resque
102
110
  end
103
111
 
104
112
  # Handles queueing delayed items
105
- def handle_delayed_items
106
- item = nil
107
- if timestamp = Resque.next_delayed_timestamp
113
+ def handle_delayed_items(at_time = nil)
114
+ if timestamp = Resque.next_delayed_timestamp(at_time)
108
115
  procline "Processing Delayed Items"
109
116
  while !timestamp.nil?
110
117
  enqueue_delayed_items_for_timestamp(timestamp)
111
- timestamp = Resque.next_delayed_timestamp
118
+ timestamp = Resque.next_delayed_timestamp(at_time)
112
119
  end
113
120
  end
114
121
  end
@@ -178,24 +185,17 @@ module Resque
178
185
  end
179
186
 
180
187
  def update_schedule
181
- schedule_from_redis = Resque.get_schedules
182
- if !schedule_from_redis.nil? && schedule_from_redis != Resque.schedule
188
+ if Resque.redis.scard(:schedules_changed) > 0
183
189
  procline "Updating schedule"
184
- # unload schedules that no longer exist
185
- (Resque.schedule.keys - schedule_from_redis.keys).each do |name|
186
- unschedule_job(name)
187
- end
188
-
189
- # find changes and stop and reload or add new
190
- schedule_from_redis.each do |name, config|
191
- if (Resque.schedule[name].nil? || Resque.schedule[name].empty?) || (config != Resque.schedule[name])
192
- unschedule_job(name)
193
- load_schedule_job(name, config)
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)
194
197
  end
195
198
  end
196
-
197
- # load new schedule into Resque.schedule
198
- Resque.schedule = schedule_from_redis
199
199
  end
200
200
  procline "Schedules Loaded"
201
201
  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,11 @@ 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
58
69
  config
59
70
  end
60
71
 
@@ -66,6 +77,7 @@ module ResqueScheduler
66
77
  # remove a given schedule by name
67
78
  def remove_schedule(name)
68
79
  redis.hdel(:schedules, name)
80
+ redis.sadd(:schedules_changed, name)
69
81
  end
70
82
 
71
83
  # This method is nearly identical to +enqueue+ only it also
@@ -123,8 +135,8 @@ module ResqueScheduler
123
135
 
124
136
  # Returns the next delayed queue timestamp
125
137
  # (don't call directly)
126
- def next_delayed_timestamp
127
- 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]
128
140
  timestamp = items.nil? ? nil : Array(items).first
129
141
  timestamp.to_i unless timestamp.nil?
130
142
  end
@@ -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.11'
2
+ Version = '1.10.12'
3
3
  end
@@ -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
 
@@ -151,6 +158,7 @@ class Resque::SchedulerTest < Test::Unit::TestCase
151
158
  end
152
159
 
153
160
  def test_update_schedule_with_mocks
161
+ Resque::Scheduler.dynamic = true
154
162
  Resque.schedule = {
155
163
  "some_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"},
156
164
  "another_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/5"},
@@ -162,15 +170,16 @@ class Resque::SchedulerTest < Test::Unit::TestCase
162
170
  Resque::Scheduler.rufus_scheduler.expects(:unschedule).with(Resque::Scheduler.scheduled_jobs["some_ivar_job"].job_id)
163
171
  Resque::Scheduler.rufus_scheduler.expects(:unschedule).with(Resque::Scheduler.scheduled_jobs["another_ivar_job"].job_id)
164
172
 
165
- Resque.redis.hset(:schedules, "some_ivar_job", Resque.encode(
173
+ Resque.set_schedule("some_ivar_job",
166
174
  {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2"}
167
- ))
168
- Resque.redis.hset(:schedules, "new_ivar_job", Resque.encode(
175
+ )
176
+ Resque.set_schedule("new_ivar_job",
169
177
  {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp/3"}
170
- ))
171
- Resque.redis.hset(:schedules, "stay_put_job", Resque.encode(
178
+ )
179
+ Resque.set_schedule("stay_put_job",
172
180
  {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp"}
173
- ))
181
+ )
182
+ Resque.remove_schedule("another_ivar_job")
174
183
 
175
184
  Resque::Scheduler.update_schedule
176
185
 
@@ -183,12 +192,22 @@ class Resque::SchedulerTest < Test::Unit::TestCase
183
192
  assert !Resque.schedule.keys.include?("another_ivar_job")
184
193
  end
185
194
 
195
+ def test_set_schedules
196
+ Resque::Scheduler.dynamic = true
197
+ Resque.schedule = {"my_ivar_job" => {
198
+ 'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/75"
199
+ }}
200
+ assert_equal({'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/75"},
201
+ Resque.decode(Resque.redis.hget(:schedules, "my_ivar_job")))
202
+ end
203
+
186
204
  def test_set_schedule
187
205
  Resque.set_schedule("some_ivar_job", {
188
206
  'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/22"
189
207
  })
190
208
  assert_equal({'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/22"},
191
209
  Resque.decode(Resque.redis.hget(:schedules, "some_ivar_job")))
210
+ assert Resque.redis.sismember(:schedules_changed, "some_ivar_job")
192
211
  end
193
212
 
194
213
  def test_get_schedule
@@ -205,11 +224,13 @@ class Resque::SchedulerTest < Test::Unit::TestCase
205
224
  ))
206
225
  Resque.remove_schedule("some_ivar_job3")
207
226
  assert_equal nil, Resque.redis.hget(:schedules, "some_ivar_job3")
227
+ assert Resque.redis.sismember(:schedules_changed, "some_ivar_job3")
208
228
  end
209
229
 
210
230
  def test_adheres_to_lint
211
231
  assert_nothing_raised do
212
232
  Resque::Plugin.lint(Resque::Scheduler)
233
+ Resque::Plugin.lint(ResqueScheduler)
213
234
  end
214
235
  end
215
236
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brianjlandau-resque-scheduler
3
3
  version: !ruby/object:Gem::Version
4
- hash: 41
4
+ hash: 39
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
8
  - 10
9
- - 11
10
- version: 1.10.11
9
+ - 12
10
+ version: 1.10.12
11
11
  platform: ruby
12
12
  authors:
13
13
  - Ben VandenBos
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-11-06 00:00:00 -04:00
19
+ date: 2010-11-11 00:00:00 -05:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency