brianjlandau-resque-scheduler 1.10.2 → 1.10.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +3 -12
- data/brianjlandau-resque-scheduler.gemspec +2 -2
- data/lib/resque/scheduler.rb +58 -14
- data/lib/resque_scheduler.rb +5 -0
- data/lib/resque_scheduler/version.rb +1 -1
- data/test/scheduler_test.rb +95 -8
- metadata +4 -4
data/README.markdown
CHANGED
@@ -140,20 +140,11 @@ environment variable, the job won't be loaded.
|
|
140
140
|
|
141
141
|
### Dynamic Schedules
|
142
142
|
|
143
|
-
If you
|
143
|
+
If needed you can also have schedules that are dynamically defined and updated inside of your application. This can be completed by loading the schedule initially wherever you configure Resque and setting `Resque::Scheduler.dynamic` to `true`. Then subsequently updating the "`schedule`" key in redis (namespaced to the Resque namespace) with a JSON encoded version of the schedule hash will cause the schedule to be updated.
|
144
144
|
|
145
|
-
|
146
|
-
def self.reload_schedule!
|
147
|
-
self.schedule = MyScheduleModel.all.inject({}) {|schedule_hash, record|
|
148
|
-
schedule_hash[record.name.to_sym] = record.attributes.select{|key, value| key != 'name' }
|
149
|
-
schedule_hash
|
150
|
-
}
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
Resque.reload_schedule!
|
145
|
+
When the scheduler loops it will look for differences between the existing schedule and the current schedule in redis. If there are differences it will make the necessary changes to the running schedule.
|
155
146
|
|
156
|
-
To
|
147
|
+
To force the scheduler to reload the schedule you just send it the `USR2` signal.
|
157
148
|
|
158
149
|
### Support for customized Job classes
|
159
150
|
|
@@ -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.3"
|
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-10-
|
12
|
+
s.date = %q{2010-10-20}
|
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
@@ -14,6 +14,14 @@ module Resque
|
|
14
14
|
|
15
15
|
# If set, produces no output
|
16
16
|
attr_accessor :mute
|
17
|
+
|
18
|
+
# If set, will try to update the schulde in the loop
|
19
|
+
attr_accessor :dynamic
|
20
|
+
|
21
|
+
# the Rufus::Scheduler jobs that are scheduled
|
22
|
+
def scheduled_jobs
|
23
|
+
@@scheduled_jobs
|
24
|
+
end
|
17
25
|
|
18
26
|
# Schedule all jobs and continually look for delayed jobs (never returns)
|
19
27
|
def run
|
@@ -27,6 +35,7 @@ module Resque
|
|
27
35
|
# Now start the scheduling part of the loop.
|
28
36
|
loop do
|
29
37
|
handle_delayed_items
|
38
|
+
update_schedule if dynamic
|
30
39
|
poll_sleep
|
31
40
|
end
|
32
41
|
|
@@ -53,22 +62,29 @@ module Resque
|
|
53
62
|
# rufus scheduler instance
|
54
63
|
def load_schedule!
|
55
64
|
log! "Schedule empty! Set Resque.schedule" if Resque.schedule.empty?
|
56
|
-
|
65
|
+
|
66
|
+
@@scheduled_jobs = {}
|
67
|
+
|
57
68
|
Resque.schedule.each do |name, config|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
load_schedule_job(name, config)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Loads a job schedule into the Rufus::Scheduler and stores it in @@scheduled_jobs
|
74
|
+
def load_schedule_job(name, config)
|
75
|
+
# If rails_env is set in the config, enforce ENV['RAILS_ENV'] as
|
76
|
+
# required for the jobs to be scheduled. If rails_env is missing, the
|
77
|
+
# job should be scheduled regardless of what ENV['RAILS_ENV'] is set
|
78
|
+
# to.
|
79
|
+
if config['rails_env'].nil? || rails_env_matches?(config)
|
80
|
+
log! "Scheduling #{name} "
|
81
|
+
if !config['cron'].nil? && config['cron'].length > 0
|
82
|
+
@@scheduled_jobs[name] = rufus_scheduler.cron config['cron'] do
|
83
|
+
log! "queuing #{config['class']} (#{name})"
|
84
|
+
enqueue_from_config(config)
|
71
85
|
end
|
86
|
+
else
|
87
|
+
log! "no cron found for #{config['class']} (#{name}) - skipping"
|
72
88
|
end
|
73
89
|
end
|
74
90
|
end
|
@@ -150,6 +166,34 @@ module Resque
|
|
150
166
|
Resque.reload_schedule!
|
151
167
|
load_schedule!
|
152
168
|
end
|
169
|
+
|
170
|
+
def update_schedule
|
171
|
+
schedule_from_redis = Resque.decode(Resque.redis.get(:schedule))
|
172
|
+
if !schedule_from_redis.nil? && !schedule_from_redis.empty? && schedule_from_redis != Resque.schedule
|
173
|
+
# unload schedules that no longer exist
|
174
|
+
(Resque.schedule.keys - schedule_from_redis.keys).each do |name|
|
175
|
+
unschedule_job(name)
|
176
|
+
end
|
177
|
+
|
178
|
+
# find changes and stop and reload or add new
|
179
|
+
schedule_from_redis.each do |name, config|
|
180
|
+
if (Resque.schedule[name].nil? || Resque.schedule[name].empty?) || (config != Resque.schedule[name])
|
181
|
+
unschedule_job(name)
|
182
|
+
load_schedule_job(name, config)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# load new schedule into Resque.schedule
|
187
|
+
Resque.schedule = schedule_from_redis
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def unschedule_job(name)
|
192
|
+
if scheduled_jobs[name]
|
193
|
+
scheduled_jobs[name].unschedule
|
194
|
+
@@scheduled_jobs.delete(name)
|
195
|
+
end
|
196
|
+
end
|
153
197
|
|
154
198
|
# Sleeps and returns true
|
155
199
|
def poll_sleep
|
data/lib/resque_scheduler.rb
CHANGED
@@ -33,6 +33,11 @@ module ResqueScheduler
|
|
33
33
|
def schedule
|
34
34
|
@schedule ||= {}
|
35
35
|
end
|
36
|
+
|
37
|
+
# reloads the schedule from redis
|
38
|
+
def reload_schedule!
|
39
|
+
@schedule = decode(redis.get(:schedule))
|
40
|
+
end
|
36
41
|
|
37
42
|
# This method is nearly identical to +enqueue+ only it also
|
38
43
|
# takes a timestamp which will be used to schedule the job
|
data/test/scheduler_test.rb
CHANGED
@@ -1,11 +1,5 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/test_helper'
|
2
2
|
|
3
|
-
module ::Resque
|
4
|
-
def self.reload_schedule!
|
5
|
-
self.schedule = {:some_ivar_job2 => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2"}}
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
3
|
class Resque::SchedulerTest < Test::Unit::TestCase
|
10
4
|
|
11
5
|
class FakeJob
|
@@ -13,7 +7,9 @@ class Resque::SchedulerTest < Test::Unit::TestCase
|
|
13
7
|
end
|
14
8
|
|
15
9
|
def setup
|
10
|
+
Resque::Scheduler.mute = true
|
16
11
|
Resque::Scheduler.clear_schedule!
|
12
|
+
Resque::Scheduler.send(:class_variable_set, :@@scheduled_jobs, {})
|
17
13
|
end
|
18
14
|
|
19
15
|
def test_enqueue_from_config_puts_stuff_in_the_resque_queue_without_class_loaded
|
@@ -76,19 +72,110 @@ class Resque::SchedulerTest < Test::Unit::TestCase
|
|
76
72
|
Resque::Scheduler.load_schedule!
|
77
73
|
|
78
74
|
assert_equal(1, Resque::Scheduler.rufus_scheduler.all_jobs.size)
|
75
|
+
assert Resque::Scheduler.scheduled_jobs.include?(:some_ivar_job)
|
79
76
|
end
|
80
77
|
|
81
78
|
def test_can_reload_schedule
|
82
|
-
Resque.schedule = {
|
79
|
+
Resque.schedule = {"some_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"}}
|
80
|
+
Resque.redis.set(:schedule, Resque.encode({
|
81
|
+
"some_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"}
|
82
|
+
}))
|
83
|
+
|
83
84
|
Resque::Scheduler.load_schedule!
|
84
85
|
|
85
86
|
assert_equal(1, Resque::Scheduler.rufus_scheduler.all_jobs.size)
|
87
|
+
assert Resque::Scheduler.scheduled_jobs.include?("some_ivar_job")
|
88
|
+
|
89
|
+
Resque.redis.set(:schedule, Resque.encode({
|
90
|
+
"some_ivar_job2" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2"}
|
91
|
+
}))
|
86
92
|
|
87
93
|
Resque::Scheduler.reload_schedule!
|
88
94
|
|
89
95
|
assert_equal(1, Resque::Scheduler.rufus_scheduler.all_jobs.size)
|
90
96
|
|
91
|
-
assert_equal '/tmp/2', Resque.schedule[
|
97
|
+
assert_equal '/tmp/2', Resque.schedule["some_ivar_job2"]["args"]
|
98
|
+
assert Resque::Scheduler.scheduled_jobs.include?("some_ivar_job2")
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_load_schedule_job
|
102
|
+
Resque::Scheduler.load_schedule_job("some_ivar_job", {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"})
|
103
|
+
|
104
|
+
assert_equal(1, Resque::Scheduler.rufus_scheduler.all_jobs.size)
|
105
|
+
assert_equal(1, Resque::Scheduler.scheduled_jobs.size)
|
106
|
+
assert Resque::Scheduler.scheduled_jobs.keys.include?("some_ivar_job")
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_load_schedule_job_with_no_cron
|
110
|
+
Resque::Scheduler.load_schedule_job("some_ivar_job", {'class' => 'SomeIvarJob', 'args' => "/tmp"})
|
111
|
+
|
112
|
+
assert_equal(0, Resque::Scheduler.rufus_scheduler.all_jobs.size)
|
113
|
+
assert_equal(0, Resque::Scheduler.scheduled_jobs.size)
|
114
|
+
assert !Resque::Scheduler.scheduled_jobs.keys.include?("some_ivar_job")
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_load_schedule_job_with_blank_cron
|
118
|
+
Resque::Scheduler.load_schedule_job("some_ivar_job", {'cron' => '', 'class' => 'SomeIvarJob', 'args' => "/tmp"})
|
119
|
+
|
120
|
+
assert_equal(0, Resque::Scheduler.rufus_scheduler.all_jobs.size)
|
121
|
+
assert_equal(0, Resque::Scheduler.scheduled_jobs.size)
|
122
|
+
assert !Resque::Scheduler.scheduled_jobs.keys.include?("some_ivar_job")
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_update_schedule
|
126
|
+
Resque.schedule = {
|
127
|
+
"some_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"},
|
128
|
+
"another_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/5"},
|
129
|
+
"stay_put_job" => {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp"}
|
130
|
+
}
|
131
|
+
|
132
|
+
Resque::Scheduler.load_schedule!
|
133
|
+
|
134
|
+
Resque.redis.set(:schedule, Resque.encode({
|
135
|
+
"some_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2"},
|
136
|
+
"new_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp/3"},
|
137
|
+
"stay_put_job" => {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp"}
|
138
|
+
}))
|
139
|
+
|
140
|
+
Resque::Scheduler.update_schedule
|
141
|
+
|
142
|
+
assert_equal(3, Resque::Scheduler.rufus_scheduler.all_jobs.size)
|
143
|
+
assert_equal(3, Resque::Scheduler.scheduled_jobs.size)
|
144
|
+
%w(some_ivar_job new_ivar_job stay_put_job).each do |job_name|
|
145
|
+
assert Resque::Scheduler.scheduled_jobs.keys.include?(job_name)
|
146
|
+
assert Resque.schedule.keys.include?(job_name)
|
147
|
+
end
|
148
|
+
assert !Resque::Scheduler.scheduled_jobs.keys.include?("another_ivar_job")
|
149
|
+
assert !Resque.schedule.keys.include?("another_ivar_job")
|
150
|
+
end
|
151
|
+
|
152
|
+
def test_update_schedule_with_mocks
|
153
|
+
Resque.schedule = {
|
154
|
+
"some_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"},
|
155
|
+
"another_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/5"},
|
156
|
+
"stay_put_job" => {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp"}
|
157
|
+
}
|
158
|
+
|
159
|
+
Resque::Scheduler.load_schedule!
|
160
|
+
|
161
|
+
Resque::Scheduler.rufus_scheduler.expects(:unschedule).with(Resque::Scheduler.scheduled_jobs["some_ivar_job"].job_id)
|
162
|
+
Resque::Scheduler.rufus_scheduler.expects(:unschedule).with(Resque::Scheduler.scheduled_jobs["another_ivar_job"].job_id)
|
163
|
+
|
164
|
+
Resque.redis.set(:schedule, Resque.encode({
|
165
|
+
"some_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2"},
|
166
|
+
"new_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp/3"},
|
167
|
+
"stay_put_job" => {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp"}
|
168
|
+
}))
|
169
|
+
|
170
|
+
Resque::Scheduler.update_schedule
|
171
|
+
|
172
|
+
assert_equal(3, Resque::Scheduler.scheduled_jobs.size)
|
173
|
+
%w(some_ivar_job new_ivar_job stay_put_job).each do |job_name|
|
174
|
+
assert Resque::Scheduler.scheduled_jobs.keys.include?(job_name)
|
175
|
+
assert Resque.schedule.keys.include?(job_name)
|
176
|
+
end
|
177
|
+
assert !Resque::Scheduler.scheduled_jobs.keys.include?("another_ivar_job")
|
178
|
+
assert !Resque.schedule.keys.include?("another_ivar_job")
|
92
179
|
end
|
93
180
|
|
94
181
|
def test_adheres_to_lint
|
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: 57
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 10
|
9
|
-
-
|
10
|
-
version: 1.10.
|
9
|
+
- 3
|
10
|
+
version: 1.10.3
|
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-10-
|
19
|
+
date: 2010-10-20 00:00:00 -04:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|