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.
- data/lib/resque/scheduler.rb +43 -30
- data/lib/resque_scheduler/server/views/scheduler.erb +4 -2
- data/lib/resque_scheduler/version.rb +1 -1
- data/lib/resque_scheduler.rb +16 -3
- data/sskirby-resque-scheduler.gemspec +2 -2
- data/test/delayed_queue_test.rb +21 -0
- data/test/scheduler_test.rb +38 -15
- metadata +3 -3
data/lib/resque/scheduler.rb
CHANGED
@@ -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
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
-
|
87
|
-
|
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
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
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>
|
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/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,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
|
-
|
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.
|
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{
|
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.}
|
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
|
|
@@ -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.
|
174
|
+
Resque.set_schedule("some_ivar_job",
|
166
175
|
{'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2"}
|
167
|
-
)
|
168
|
-
Resque.
|
176
|
+
)
|
177
|
+
Resque.set_schedule("new_ivar_job",
|
169
178
|
{'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp/3"}
|
170
|
-
)
|
171
|
-
Resque.
|
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
|
-
version: 1.10.
|
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:
|
20
|
+
date: 2011-08-31 00:00:00 -04:00
|
21
21
|
default_executable:
|
22
22
|
dependencies:
|
23
23
|
- !ruby/object:Gem::Dependency
|