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.
- 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
|