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.
- data/brianjlandau-resque-scheduler.gemspec +2 -2
- data/lib/resque/scheduler.rb +28 -28
- data/lib/resque_scheduler.rb +15 -3
- data/lib/resque_scheduler/server/views/scheduler.erb +4 -2
- data/lib/resque_scheduler/version.rb +1 -1
- data/test/delayed_queue_test.rb +21 -0
- data/test/scheduler_test.rb +36 -15
- metadata +4 -4
@@ -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.
|
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-
|
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.}
|
data/lib/resque/scheduler.rb
CHANGED
@@ -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
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
90
|
-
|
96
|
+
interval_defined = true
|
97
|
+
break
|
91
98
|
end
|
92
|
-
|
93
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
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
|
data/lib/resque_scheduler.rb
CHANGED
@@ -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
|
-
|
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>
|
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"><%=
|
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>
|
data/test/delayed_queue_test.rb
CHANGED
@@ -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
|
data/test/scheduler_test.rb
CHANGED
@@ -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.
|
137
|
+
Resque.set_schedule("some_ivar_job",
|
132
138
|
{'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2"}
|
133
|
-
)
|
134
|
-
Resque.
|
139
|
+
)
|
140
|
+
Resque.set_schedule("new_ivar_job",
|
135
141
|
{'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp/3"}
|
136
|
-
)
|
137
|
-
Resque.
|
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.
|
173
|
+
Resque.set_schedule("some_ivar_job",
|
166
174
|
{'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2"}
|
167
|
-
)
|
168
|
-
Resque.
|
175
|
+
)
|
176
|
+
Resque.set_schedule("new_ivar_job",
|
169
177
|
{'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp/3"}
|
170
|
-
)
|
171
|
-
Resque.
|
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:
|
4
|
+
hash: 39
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 10
|
9
|
-
-
|
10
|
-
version: 1.10.
|
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-
|
19
|
+
date: 2010-11-11 00:00:00 -05:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|