resque-scheduler 2.1.0 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of resque-scheduler might be problematic. Click here for more details.
- data/.gitignore +2 -0
- data/AUTHORS.md +2 -0
- data/README.md +8 -1
- data/lib/resque/scheduler.rb +4 -1
- data/lib/resque/scheduler_locking.rb +1 -1
- data/lib/resque_scheduler.rb +34 -19
- data/lib/resque_scheduler/server.rb +24 -1
- data/lib/resque_scheduler/server/views/requeue-params.erb +23 -0
- data/lib/resque_scheduler/server/views/scheduler.erb +2 -2
- data/lib/resque_scheduler/version.rb +1 -1
- data/resque-scheduler.gemspec +2 -2
- data/test/resque-web_test.rb +84 -0
- data/test/scheduler_test.rb +2 -2
- metadata +9 -14
data/AUTHORS.md
CHANGED
@@ -21,6 +21,7 @@ Resque Scheduler authors
|
|
21
21
|
- Denis Yagofarov
|
22
22
|
- Evan Tahler
|
23
23
|
- Giovanni Cappellotto
|
24
|
+
- Harry Lascelles
|
24
25
|
- Henrik Nyh
|
25
26
|
- James Le Cuirot
|
26
27
|
- John Crepezzi
|
@@ -47,6 +48,7 @@ Resque Scheduler authors
|
|
47
48
|
- Ryan Biesemeyer
|
48
49
|
- Ryan Carver
|
49
50
|
- Sebastian Kippe
|
51
|
+
- Spring MC
|
50
52
|
- Tim Liner
|
51
53
|
- Tony Lewis
|
52
54
|
- V Sreekanth
|
data/README.md
CHANGED
@@ -319,6 +319,13 @@ scheduled jobs, however, will not fire upon recovery of the scheduler process.
|
|
319
319
|
Think of scheduled (recurring) jobs as cron jobs - if you stop cron, it doesn't fire
|
320
320
|
missed jobs once it starts back up.
|
321
321
|
|
322
|
+
You might want to share a redis instance amongst multiple Rails applications with different
|
323
|
+
scheduler with different config yaml files. If this is the case, normally, only one will ever
|
324
|
+
run, leading to undesired behaviour. To allow different scheduler configs run at the same time
|
325
|
+
on one redis, you can either namespace your redis connections, or supply an environment variable
|
326
|
+
to split the shared lock key resque-scheduler uses thus:
|
327
|
+
|
328
|
+
RESQUE_SCHEDULER_MASTER_LOCK_PREFIX=MyApp: rake resque:scheduler
|
322
329
|
|
323
330
|
### resque-web Additions
|
324
331
|
|
@@ -328,7 +335,7 @@ the delayed queue.
|
|
328
335
|
|
329
336
|
The Schedule tab:
|
330
337
|
|
331
|
-
![The Schedule Tab](
|
338
|
+
![The Schedule Tab](https://f.cloud.github.com/assets/45143/1178456/c99e5568-21b0-11e3-8c57-e1305d0ee8ef.png)
|
332
339
|
|
333
340
|
The Delayed tab:
|
334
341
|
|
data/lib/resque/scheduler.rb
CHANGED
@@ -44,6 +44,7 @@ module Resque
|
|
44
44
|
# Schedule all jobs and continually look for delayed jobs (never returns)
|
45
45
|
def run
|
46
46
|
$0 = "resque-scheduler: Starting"
|
47
|
+
|
47
48
|
# trap signals
|
48
49
|
register_signal_handlers
|
49
50
|
|
@@ -307,10 +308,12 @@ module Resque
|
|
307
308
|
true
|
308
309
|
end
|
309
310
|
|
310
|
-
# Sets the shutdown flag, exits if sleeping
|
311
|
+
# Sets the shutdown flag, clean schedules and exits if sleeping
|
311
312
|
def shutdown
|
312
313
|
@shutdown = true
|
314
|
+
|
313
315
|
if @sleeping
|
316
|
+
Resque.clean_schedules
|
314
317
|
Thread.new { release_master_lock! }
|
315
318
|
exit
|
316
319
|
end
|
data/lib/resque_scheduler.rb
CHANGED
@@ -25,7 +25,7 @@ module ResqueScheduler
|
|
25
25
|
# is used implicitly as "class" argument - in the "MakeTea" example,
|
26
26
|
# "MakeTea" is used both as job name and resque worker class.
|
27
27
|
#
|
28
|
-
# Any jobs that were in the old schedule, but are not
|
28
|
+
# Any jobs that were in the old schedule, but are not
|
29
29
|
# present in the new schedule, will be removed.
|
30
30
|
#
|
31
31
|
# :cron can be any cron scheduling string
|
@@ -46,23 +46,27 @@ module ResqueScheduler
|
|
46
46
|
# params is an array, each element in the array is passed as a separate
|
47
47
|
# param, otherwise params is passed in as the only parameter to perform.
|
48
48
|
def schedule=(schedule_hash)
|
49
|
+
# clean the schedules as it exists in redis
|
50
|
+
clean_schedules
|
51
|
+
|
49
52
|
schedule_hash = prepare_schedule(schedule_hash)
|
50
53
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
set_schedule(name, job_spec)
|
55
|
-
end
|
56
|
-
(schedule.keys - schedule_hash.keys.map(&:to_s)).each do |name|
|
57
|
-
remove_schedule(name)
|
58
|
-
end
|
54
|
+
# store all schedules in redis, so we can retrieve them back everywhere.
|
55
|
+
schedule_hash.each do |name, job_spec|
|
56
|
+
set_schedule(name, job_spec)
|
59
57
|
end
|
60
|
-
|
58
|
+
|
59
|
+
# ensure only return the successfully saved data!
|
60
|
+
reload_schedule!
|
61
61
|
end
|
62
62
|
|
63
63
|
# Returns the schedule hash
|
64
64
|
def schedule
|
65
|
-
@schedule ||=
|
65
|
+
@schedule ||= get_schedules
|
66
|
+
if @schedule.nil?
|
67
|
+
return {}
|
68
|
+
end
|
69
|
+
@schedule
|
66
70
|
end
|
67
71
|
|
68
72
|
# reloads the schedule from redis
|
@@ -70,17 +74,28 @@ module ResqueScheduler
|
|
70
74
|
@schedule = get_schedules
|
71
75
|
end
|
72
76
|
|
73
|
-
# gets the
|
77
|
+
# gets the schedules as it exists in redis
|
74
78
|
def get_schedules
|
79
|
+
unless redis.exists(:schedules)
|
80
|
+
return nil
|
81
|
+
end
|
82
|
+
|
83
|
+
redis.hgetall(:schedules).tap do |h|
|
84
|
+
h.each do |name, config|
|
85
|
+
h[name] = decode(config)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# clean the schedules as it exists in redis, useful for first setup?
|
91
|
+
def clean_schedules
|
75
92
|
if redis.exists(:schedules)
|
76
|
-
redis.
|
77
|
-
|
78
|
-
h[name] = decode(config)
|
79
|
-
end
|
93
|
+
redis.hkeys(:schedules).each do |key|
|
94
|
+
remove_schedule(key)
|
80
95
|
end
|
81
|
-
else
|
82
|
-
nil
|
83
96
|
end
|
97
|
+
@schedule = nil
|
98
|
+
true
|
84
99
|
end
|
85
100
|
|
86
101
|
# Create or update a schedule with the provided name and configuration.
|
@@ -314,7 +329,7 @@ module ResqueScheduler
|
|
314
329
|
redis.unwatch
|
315
330
|
end
|
316
331
|
end
|
317
|
-
|
332
|
+
|
318
333
|
def validate_job!(klass)
|
319
334
|
if klass.to_s.empty?
|
320
335
|
raise Resque::NoClassError.new("Jobs must be given a class.")
|
@@ -27,7 +27,30 @@ module ResqueScheduler
|
|
27
27
|
end
|
28
28
|
|
29
29
|
post "/schedule/requeue" do
|
30
|
-
|
30
|
+
@job_name = params['job_name'] || params[:job_name]
|
31
|
+
config = Resque.schedule[@job_name]
|
32
|
+
@parameters = config['parameters'] || config[:parameters]
|
33
|
+
if @parameters
|
34
|
+
erb File.read(File.join(File.dirname(__FILE__), 'server/views/requeue-params.erb'))
|
35
|
+
else
|
36
|
+
Resque::Scheduler.enqueue_from_config(config)
|
37
|
+
redirect u("/overview")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
post "/schedule/requeue_with_params" do
|
42
|
+
job_name = params['job_name'] || params[:job_name]
|
43
|
+
config = Resque.schedule[job_name]
|
44
|
+
# Build args hash from post data (removing the job name)
|
45
|
+
submitted_args = params.reject {|key, value| key == 'job_name' || key == :job_name}
|
46
|
+
|
47
|
+
# Merge constructed args hash with existing args hash for
|
48
|
+
# the job, if it exists
|
49
|
+
config_args = config['args'] || config[:args] || {}
|
50
|
+
config_args = config_args.merge(submitted_args)
|
51
|
+
|
52
|
+
# Insert the args hash into config and queue the resque job
|
53
|
+
config = config.merge({'args' => config_args})
|
31
54
|
Resque::Scheduler.enqueue_from_config(config)
|
32
55
|
redirect u("/overview")
|
33
56
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<h1><%= @job_name %></h1>
|
2
|
+
|
3
|
+
<p class='intro'>
|
4
|
+
This job requires parameters:
|
5
|
+
</p>
|
6
|
+
|
7
|
+
<form style="float:left" action="<%= u "/schedule/requeue_with_params" %>" method="post">
|
8
|
+
<table>
|
9
|
+
<% @parameters.each do |key, value| %>
|
10
|
+
<% value ||= {} %>
|
11
|
+
<tr>
|
12
|
+
<td>
|
13
|
+
<%= key %>
|
14
|
+
<% if value['description'] || value[:description] %>
|
15
|
+
<span style="border-bottom:1px dotted;" title="<%=value ['description'] || value[:description] %>">(?)</span>
|
16
|
+
<% end %>:
|
17
|
+
</td>
|
18
|
+
<td><input type="text" name="<%= key %>" value="<%= value['default'] || value[:default] %>"></td>
|
19
|
+
<% end %>
|
20
|
+
</table>
|
21
|
+
<input type="hidden" name="job_name" value="<%= @job_name %>">
|
22
|
+
<input type="submit" value="Queue now">
|
23
|
+
</form>
|
@@ -27,10 +27,10 @@
|
|
27
27
|
</td>
|
28
28
|
<td><%= h name %></td>
|
29
29
|
<td><%= h config['description'] %></td>
|
30
|
-
<td style="white-space:nowrap"><%= if !config['every'].nil?
|
30
|
+
<td style="white-space:nowrap"><%= if !config['every'].nil?
|
31
31
|
h('every: ' + (config['every'].is_a?(Array) ? config['every'].join(", ") : config['every'].to_s ))
|
32
32
|
elsif !config['cron'].nil?
|
33
|
-
h('cron: ' + config['cron'].to_s)
|
33
|
+
h('cron: ' + config['cron'].to_s)
|
34
34
|
else
|
35
35
|
'Not currently scheduled'
|
36
36
|
end %></td>
|
data/resque-scheduler.gemspec
CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency 'json' if RUBY_VERSION < '1.9'
|
27
27
|
spec.add_development_dependency 'rubocop' unless RUBY_VERSION < '1.9'
|
28
28
|
|
29
|
-
spec.add_runtime_dependency 'redis', '>=
|
29
|
+
spec.add_runtime_dependency 'redis', '>= 3.0.0'
|
30
30
|
spec.add_runtime_dependency 'resque', ['>= 1.20.0', '< 1.25']
|
31
|
-
spec.add_runtime_dependency 'rufus-scheduler', '
|
31
|
+
spec.add_runtime_dependency 'rufus-scheduler', '~> 2.0'
|
32
32
|
end
|
data/test/resque-web_test.rb
CHANGED
@@ -30,3 +30,87 @@ context "on GET to /delayed" do
|
|
30
30
|
|
31
31
|
should_respond_with_success
|
32
32
|
end
|
33
|
+
|
34
|
+
def resque_schedule
|
35
|
+
{
|
36
|
+
'job_without_params' => {
|
37
|
+
'cron' => "* * * * *",
|
38
|
+
'class' => 'JobWithoutParams',
|
39
|
+
'args' => {"host" => 'localhost'},
|
40
|
+
'rails_env' => 'production'},
|
41
|
+
'job_with_params' => {
|
42
|
+
'cron' => "* * * * *",
|
43
|
+
'class' => 'JobWithParams',
|
44
|
+
'args' => {"host" => 'localhost'},
|
45
|
+
'parameters' => {
|
46
|
+
'log_level' => {
|
47
|
+
'description' => 'The level of logging',
|
48
|
+
'default' => 'warn'
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
context "POST /schedule/requeue" do
|
56
|
+
setup do
|
57
|
+
Resque.schedule = resque_schedule
|
58
|
+
Resque::Scheduler.load_schedule!
|
59
|
+
end
|
60
|
+
|
61
|
+
test 'job without params' do
|
62
|
+
# Regular jobs without params should redirect to /overview
|
63
|
+
job_name = 'job_without_params'
|
64
|
+
Resque::Scheduler.stubs(:enqueue_from_config).once.with(Resque.schedule[job_name])
|
65
|
+
|
66
|
+
post '/schedule/requeue', {'job_name' => job_name}
|
67
|
+
follow_redirect!
|
68
|
+
assert_equal "http://example.org/overview", last_request.url
|
69
|
+
assert last_response.ok?
|
70
|
+
end
|
71
|
+
|
72
|
+
test 'job with params' do
|
73
|
+
# If a job has params defined,
|
74
|
+
# it should render the template with a form for the job params
|
75
|
+
job_name = 'job_with_params'
|
76
|
+
post '/schedule/requeue', {'job_name' => job_name}
|
77
|
+
|
78
|
+
assert last_response.ok?, last_response.errors
|
79
|
+
assert last_response.body.include?("This job requires parameters")
|
80
|
+
assert last_response.body.include?("<input type=\"hidden\" name=\"job_name\" value=\"#{job_name}\">")
|
81
|
+
|
82
|
+
Resque.schedule[job_name]['parameters'].each do |param_name, param_config|
|
83
|
+
assert last_response.body.include?(
|
84
|
+
"<span style=\"border-bottom:1px dotted;\" title=\"#{param_config['description']}\">(?)</span>")
|
85
|
+
assert last_response.body.include?(
|
86
|
+
"<input type=\"text\" name=\"log_level\" value=\"#{param_config['default']}\">")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "POST /schedule/requeue_with_params" do
|
92
|
+
setup do
|
93
|
+
Resque.schedule = resque_schedule
|
94
|
+
Resque::Scheduler.load_schedule!
|
95
|
+
end
|
96
|
+
|
97
|
+
test 'job with params' do
|
98
|
+
job_name = 'job_with_params'
|
99
|
+
log_level = 'error'
|
100
|
+
|
101
|
+
job_config = Resque.schedule[job_name]
|
102
|
+
args = job_config['args'].merge({'log_level' => log_level})
|
103
|
+
job_config = job_config.merge({'args' => args})
|
104
|
+
|
105
|
+
Resque::Scheduler.stubs(:enqueue_from_config).once.with(job_config)
|
106
|
+
|
107
|
+
post '/schedule/requeue_with_params', {
|
108
|
+
'job_name' => job_name,
|
109
|
+
'log_level' => log_level
|
110
|
+
}
|
111
|
+
follow_redirect!
|
112
|
+
assert_equal "http://example.org/overview", last_request.url
|
113
|
+
|
114
|
+
assert last_response.ok?, last_response.errors
|
115
|
+
end
|
116
|
+
end
|
data/test/scheduler_test.rb
CHANGED
@@ -46,7 +46,7 @@ context "Resque::Scheduler" do
|
|
46
46
|
Resque::Scheduler.load_schedule!
|
47
47
|
|
48
48
|
assert_equal(1, Resque::Scheduler.rufus_scheduler.all_jobs.size)
|
49
|
-
assert Resque::Scheduler.scheduled_jobs.include?(
|
49
|
+
assert Resque::Scheduler.scheduled_jobs.include?('some_ivar_job')
|
50
50
|
end
|
51
51
|
|
52
52
|
test "can reload schedule" do
|
@@ -191,7 +191,7 @@ context "Resque::Scheduler" do
|
|
191
191
|
assert_equal({'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/75"},
|
192
192
|
Resque.decode(Resque.redis.hget(:schedules, "my_ivar_job")))
|
193
193
|
end
|
194
|
-
|
194
|
+
|
195
195
|
test "schedule= removes schedules not present in the given schedule argument" do
|
196
196
|
Resque::Scheduler.dynamic = true
|
197
197
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resque-scheduler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-10-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -98,7 +98,7 @@ dependencies:
|
|
98
98
|
requirements:
|
99
99
|
- - ! '>='
|
100
100
|
- !ruby/object:Gem::Version
|
101
|
-
version:
|
101
|
+
version: 3.0.0
|
102
102
|
type: :runtime
|
103
103
|
prerelease: false
|
104
104
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -106,7 +106,7 @@ dependencies:
|
|
106
106
|
requirements:
|
107
107
|
- - ! '>='
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
109
|
+
version: 3.0.0
|
110
110
|
- !ruby/object:Gem::Dependency
|
111
111
|
name: resque
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
@@ -134,17 +134,17 @@ dependencies:
|
|
134
134
|
requirement: !ruby/object:Gem::Requirement
|
135
135
|
none: false
|
136
136
|
requirements:
|
137
|
-
- -
|
137
|
+
- - ~>
|
138
138
|
- !ruby/object:Gem::Version
|
139
|
-
version: '0'
|
139
|
+
version: '2.0'
|
140
140
|
type: :runtime
|
141
141
|
prerelease: false
|
142
142
|
version_requirements: !ruby/object:Gem::Requirement
|
143
143
|
none: false
|
144
144
|
requirements:
|
145
|
-
- -
|
145
|
+
- - ~>
|
146
146
|
- !ruby/object:Gem::Version
|
147
|
-
version: '0'
|
147
|
+
version: '2.0'
|
148
148
|
description: ! "Light weight job scheduling on top of Resque.\n Adds methods enqueue_at/enqueue_in
|
149
149
|
to schedule jobs in the future.\n Also supports queueing jobs on a fixed, cron-like
|
150
150
|
schedule."
|
@@ -176,6 +176,7 @@ files:
|
|
176
176
|
- lib/resque_scheduler/server.rb
|
177
177
|
- lib/resque_scheduler/server/views/delayed.erb
|
178
178
|
- lib/resque_scheduler/server/views/delayed_timestamp.erb
|
179
|
+
- lib/resque_scheduler/server/views/requeue-params.erb
|
179
180
|
- lib/resque_scheduler/server/views/scheduler.erb
|
180
181
|
- lib/resque_scheduler/tasks.rb
|
181
182
|
- lib/resque_scheduler/version.rb
|
@@ -204,18 +205,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
204
205
|
- - ! '>='
|
205
206
|
- !ruby/object:Gem::Version
|
206
207
|
version: '0'
|
207
|
-
segments:
|
208
|
-
- 0
|
209
|
-
hash: 2224918234097590124
|
210
208
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
211
209
|
none: false
|
212
210
|
requirements:
|
213
211
|
- - ! '>='
|
214
212
|
- !ruby/object:Gem::Version
|
215
213
|
version: '0'
|
216
|
-
segments:
|
217
|
-
- 0
|
218
|
-
hash: 2224918234097590124
|
219
214
|
requirements: []
|
220
215
|
rubyforge_project:
|
221
216
|
rubygems_version: 1.8.23
|