delayed_job_on_steroids 1.7.5 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.markdown +124 -0
- data/Rakefile +5 -6
- data/VERSION +1 -1
- data/delayed_job_on_steroids.gemspec +18 -12
- data/generators/delayed_job/delayed_job_generator.rb +9 -0
- data/generators/delayed_job/templates/script +5 -0
- data/generators/delayed_job_migration/templates/migration.rb +18 -14
- data/init.rb +1 -1
- data/lib/delayed_job_on_steroids.rb +10 -0
- data/lib/delayed_on_steroids/command.rb +104 -0
- data/lib/{delayed → delayed_on_steroids}/job.rb +91 -102
- data/lib/delayed_on_steroids/job_deprecations.rb +19 -0
- data/lib/{delayed → delayed_on_steroids}/message_sending.rb +5 -1
- data/lib/{delayed → delayed_on_steroids}/performable_method.rb +0 -0
- data/lib/delayed_on_steroids/tasks.rb +28 -0
- data/lib/delayed_on_steroids/worker.rb +56 -0
- data/spec/database.rb +18 -9
- data/spec/delayed_method_spec.rb +27 -2
- data/spec/job_spec.rb +133 -82
- data/spec/worker_spec.rb +15 -0
- data/tasks/jobs.rake +1 -1
- metadata +19 -13
- data/README.textile +0 -129
- data/lib/delayed/worker.rb +0 -55
- data/lib/delayed_job.rb +0 -13
- data/tasks/tasks.rb +0 -16
@@ -0,0 +1,19 @@
|
|
1
|
+
module Delayed
|
2
|
+
module JobDeprecations
|
3
|
+
def self.move_methods_to_worker(*old_methods)
|
4
|
+
old_methods.each do |old_method|
|
5
|
+
new_method = old_method.to_s.gsub("worker_", "").to_sym
|
6
|
+
define_method(old_method) do |*args|
|
7
|
+
warn "#{caller[0]}: Job's #{old_method} is deprecated. Use Worker.#{new_method} instead."
|
8
|
+
Worker.send(new_method, *args)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
move_methods_to_worker :min_priority, :min_priority=
|
14
|
+
move_methods_to_worker :max_priority, :max_priority=
|
15
|
+
move_methods_to_worker :job_types, :job_types=
|
16
|
+
move_methods_to_worker :destroy_failed_jobs, :destroy_failed_jobs=
|
17
|
+
move_methods_to_worker :worker_name, :worker_name=
|
18
|
+
end
|
19
|
+
end
|
@@ -1,7 +1,11 @@
|
|
1
1
|
module Delayed
|
2
2
|
module MessageSending
|
3
3
|
def send_later(method, *args)
|
4
|
-
Delayed::Job.enqueue
|
4
|
+
Delayed::Job.enqueue(Delayed::PerformableMethod.new(self, method.to_sym, args))
|
5
|
+
end
|
6
|
+
|
7
|
+
def send_at(time, method, *args)
|
8
|
+
Delayed::Job.enqueue(Delayed::PerformableMethod.new(self, method.to_sym, args), 0, time)
|
5
9
|
end
|
6
10
|
|
7
11
|
module ClassMethods
|
File without changes
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Re-definitions are appended to existing tasks
|
2
|
+
task :environment
|
3
|
+
|
4
|
+
namespace :jobs do
|
5
|
+
|
6
|
+
namespace :clear do
|
7
|
+
desc "Remove failed jobs from delayed_job queue"
|
8
|
+
task :failed => [:environment] do
|
9
|
+
Delayed::Job.delete_all("failed_at IS NOT NULL")
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Clear entire delayed_job queue"
|
13
|
+
task :all => [:environment] do
|
14
|
+
Delayed::Job.delete_all
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Deprecated synonim
|
19
|
+
task :clear => ['clear:all']
|
20
|
+
|
21
|
+
desc "Report delayed_job statistics"
|
22
|
+
task :stats => [:environment] do
|
23
|
+
jobs = Delayed::Job.all
|
24
|
+
puts "Active jobs : #{ jobs.count{ |job| job.locked? } }"
|
25
|
+
puts "Scheduled jobs : #{ jobs.count{ |job| not (job.locked? or job.failed?) } }"
|
26
|
+
puts "Failed stored jobs : #{ jobs.count{ |job| job.failed? } }"
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Delayed
|
2
|
+
class Worker
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
SLEEP = 5
|
6
|
+
|
7
|
+
cattr_accessor :logger
|
8
|
+
@@logger = Rails.logger if defined?(Rails)
|
9
|
+
|
10
|
+
cattr_accessor :min_priority, :max_priority
|
11
|
+
cattr_accessor :job_types
|
12
|
+
|
13
|
+
# Every worker should has a unique name. If you start worker via +delayed_job+ script, name will be generated
|
14
|
+
# automatically based on the hostname and the pid of the process.
|
15
|
+
# There is advantage to assign name manually:
|
16
|
+
# workers can safely resume working on tasks which are locked by themselves (the worker will assume that it crashed before).
|
17
|
+
cattr_accessor :name
|
18
|
+
|
19
|
+
# By default failed jobs are destroyed after too many attempts.
|
20
|
+
# If you want to keep them around (perhaps to inspect the reason for the failure), set this to false.
|
21
|
+
cattr_accessor :destroy_failed_jobs
|
22
|
+
@@destroy_failed_jobs = true
|
23
|
+
|
24
|
+
# Starts worker
|
25
|
+
def start
|
26
|
+
@@logger.info("* [#{@@name}] Starting job worker...")
|
27
|
+
|
28
|
+
trap('TERM') { @@logger.info("* [#{@@name}] Exiting..."); $exit = true }
|
29
|
+
trap('INT') { @@logger.info("* [#{@@name}] Exiting..."); $exit = true }
|
30
|
+
|
31
|
+
loop do
|
32
|
+
result = nil
|
33
|
+
|
34
|
+
realtime = Benchmark.realtime do
|
35
|
+
result = Delayed::Job.work_off
|
36
|
+
end
|
37
|
+
|
38
|
+
count = result.sum
|
39
|
+
|
40
|
+
break if $exit
|
41
|
+
|
42
|
+
if count.zero?
|
43
|
+
sleep(SLEEP)
|
44
|
+
else
|
45
|
+
@@logger.info("* [#{@@name}] #{count} jobs processed at %.4f j/s, %d failed..." % [count / realtime, result.last])
|
46
|
+
end
|
47
|
+
|
48
|
+
break if $exit
|
49
|
+
end
|
50
|
+
|
51
|
+
ensure
|
52
|
+
# When a worker is exiting, make sure we don't have any locked jobs.
|
53
|
+
Delayed::Job.update_all("locked_by = null, locked_at = null", ["locked_by = ?", @@name])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/spec/database.rb
CHANGED
@@ -7,26 +7,35 @@ gem 'sqlite3-ruby'
|
|
7
7
|
|
8
8
|
require File.dirname(__FILE__) + '/../init'
|
9
9
|
require 'spec'
|
10
|
-
|
11
|
-
|
10
|
+
|
11
|
+
Delayed::Worker.logger = Logger.new('/tmp/dj.log')
|
12
12
|
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => '/tmp/jobs.sqlite')
|
13
13
|
ActiveRecord::Migration.verbose = false
|
14
14
|
|
15
15
|
ActiveRecord::Schema.define do
|
16
16
|
|
17
17
|
create_table :delayed_jobs, :force => true do |table|
|
18
|
-
table.integer :priority, :default => 0
|
19
|
-
table.integer :attempts, :default => 0
|
20
|
-
table.text :handler
|
21
|
-
table.string :job_type
|
18
|
+
table.integer :priority, :null => false, :default => 0
|
19
|
+
table.integer :attempts, :null => false, :default => 0
|
20
|
+
table.text :handler, :null => false
|
21
|
+
table.string :job_type, :null => false
|
22
|
+
table.string :job_tag
|
22
23
|
table.string :last_error
|
23
|
-
table.datetime :run_at
|
24
|
+
table.datetime :run_at, :null => false
|
24
25
|
table.datetime :locked_at
|
25
26
|
table.string :locked_by
|
26
27
|
table.datetime :failed_at
|
27
28
|
table.timestamps
|
28
29
|
end
|
29
30
|
|
31
|
+
add_index :delayed_jobs, [:priority, :run_at]
|
32
|
+
add_index :delayed_jobs, :job_type
|
33
|
+
add_index :delayed_jobs, :job_tag
|
34
|
+
add_index :delayed_jobs, :run_at
|
35
|
+
add_index :delayed_jobs, :locked_at
|
36
|
+
add_index :delayed_jobs, :locked_by
|
37
|
+
add_index :delayed_jobs, :failed_at
|
38
|
+
|
30
39
|
create_table :stories, :force => true do |table|
|
31
40
|
table.string :text
|
32
41
|
end
|
@@ -36,8 +45,8 @@ end
|
|
36
45
|
|
37
46
|
# Purely useful for test cases...
|
38
47
|
class Story < ActiveRecord::Base
|
39
|
-
def tell; text; end
|
48
|
+
def tell; text; end
|
40
49
|
def whatever(n, _); tell*n; end
|
41
|
-
|
50
|
+
|
42
51
|
handle_asynchronously :whatever
|
43
52
|
end
|
data/spec/delayed_method_spec.rb
CHANGED
@@ -37,7 +37,10 @@ class StoryReader
|
|
37
37
|
end
|
38
38
|
|
39
39
|
describe 'random ruby objects' do
|
40
|
-
before
|
40
|
+
before do
|
41
|
+
Delayed::Worker.name = 'worker'
|
42
|
+
Delayed::Job.delete_all
|
43
|
+
end
|
41
44
|
|
42
45
|
it "should respond_to :send_later method" do
|
43
46
|
|
@@ -78,7 +81,7 @@ describe 'random ruby objects' do
|
|
78
81
|
|
79
82
|
Delayed::Job.count.should == 1
|
80
83
|
|
81
|
-
Delayed::Job.reserve_and_run_one_job
|
84
|
+
Delayed::Job.reserve_and_run_one_job.should == true
|
82
85
|
|
83
86
|
Delayed::Job.count.should == 0
|
84
87
|
|
@@ -125,4 +128,26 @@ describe 'random ruby objects' do
|
|
125
128
|
job.payload_object.perform.should == 'Once upon...'
|
126
129
|
end
|
127
130
|
|
131
|
+
context "send_at" do
|
132
|
+
it "should queue a new job" do
|
133
|
+
lambda do
|
134
|
+
"string".send_at(1.hour.from_now, :length)
|
135
|
+
end.should change { Delayed::Job.count }.by(1)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should schedule the job in the future" do
|
139
|
+
time = 1.hour.from_now
|
140
|
+
job = "string".send_at(time, :length)
|
141
|
+
job.run_at.should == time
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should store payload as PerformableMethod" do
|
145
|
+
job = "string".send_at(1.hour.from_now, :count, 'r')
|
146
|
+
job.payload_object.class.should == Delayed::PerformableMethod
|
147
|
+
job.payload_object.method.should == :count
|
148
|
+
job.payload_object.args.should == ['r']
|
149
|
+
job.payload_object.perform.should == 1
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
128
153
|
end
|
data/spec/job_spec.rb
CHANGED
@@ -8,28 +8,38 @@ end
|
|
8
8
|
class ErrorJob
|
9
9
|
cattr_accessor :runs; self.runs = 0
|
10
10
|
def perform; raise 'did not work'; end
|
11
|
-
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class LongRunningJob
|
14
|
+
def perform; sleep 250; end
|
15
|
+
end
|
12
16
|
|
13
17
|
module M
|
14
18
|
class ModuleJob
|
15
19
|
cattr_accessor :runs; self.runs = 0
|
16
|
-
def perform; @@runs += 1; end
|
20
|
+
def perform; @@runs += 1; end
|
17
21
|
end
|
18
|
-
|
19
22
|
end
|
20
23
|
|
21
24
|
describe Delayed::Job do
|
22
|
-
before do
|
23
|
-
Delayed::
|
24
|
-
Delayed::
|
25
|
-
|
25
|
+
before do
|
26
|
+
Delayed::Worker.max_priority = nil
|
27
|
+
Delayed::Worker.min_priority = nil
|
28
|
+
|
26
29
|
Delayed::Job.delete_all
|
27
30
|
end
|
28
|
-
|
31
|
+
|
29
32
|
before(:each) do
|
30
33
|
SimpleJob.runs = 0
|
31
34
|
end
|
32
35
|
|
36
|
+
it "responds to deprecated methods moved to Worker" do
|
37
|
+
Delayed::JobDeprecations.instance_methods.each do |m|
|
38
|
+
Delayed::Job.respond_to?(m).should == true
|
39
|
+
silence_warnings { Delayed::Job.send(m) } unless m.to_s =~ /=$/
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
33
43
|
it "should set run_at automatically if not set" do
|
34
44
|
Delayed::Job.create(:payload_object => ErrorJob.new ).run_at.should_not == nil
|
35
45
|
end
|
@@ -73,25 +83,29 @@ describe Delayed::Job do
|
|
73
83
|
it "should work on specified job types" do
|
74
84
|
SimpleJob.runs.should == 0
|
75
85
|
|
76
|
-
Delayed::
|
86
|
+
Delayed::Worker.job_types = "SimpleJob"
|
77
87
|
Delayed::Job.enqueue SimpleJob.new
|
78
88
|
Delayed::Job.work_off
|
79
89
|
|
80
90
|
SimpleJob.runs.should == 1
|
81
91
|
|
82
|
-
Delayed::
|
92
|
+
Delayed::Worker.job_types = nil
|
83
93
|
end
|
84
94
|
|
85
95
|
it "should not work on unspecified job types" do
|
86
96
|
SimpleJob.runs.should == 0
|
87
97
|
|
88
|
-
Delayed::
|
98
|
+
Delayed::Worker.job_types = "AnotherJob"
|
89
99
|
Delayed::Job.enqueue SimpleJob.new
|
90
100
|
Delayed::Job.work_off
|
91
101
|
|
92
102
|
SimpleJob.runs.should == 0
|
93
103
|
|
94
|
-
Delayed::
|
104
|
+
Delayed::Worker.job_types = nil
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should allow to specify job tag" do
|
108
|
+
Delayed::Job.create(:payload_object => ErrorJob.new, :job_tag => 'my tag' ).job_tag.should == 'my tag'
|
95
109
|
end
|
96
110
|
|
97
111
|
it "should work with eval jobs" do
|
@@ -106,7 +120,7 @@ describe Delayed::Job do
|
|
106
120
|
|
107
121
|
$eval_job_ran.should == true
|
108
122
|
end
|
109
|
-
|
123
|
+
|
110
124
|
it "should work with jobs in modules" do
|
111
125
|
M::ModuleJob.runs.should == 0
|
112
126
|
|
@@ -115,7 +129,7 @@ describe Delayed::Job do
|
|
115
129
|
|
116
130
|
M::ModuleJob.runs.should == 1
|
117
131
|
end
|
118
|
-
|
132
|
+
|
119
133
|
it "should re-schedule by about 1 second at first and increment this more and more minutes when it fails to execute properly" do
|
120
134
|
Delayed::Job.enqueue ErrorJob.new
|
121
135
|
Delayed::Job.work_off(1)
|
@@ -170,28 +184,52 @@ describe Delayed::Job do
|
|
170
184
|
job.should_receive(:attempt_to_load).with('Delayed::JobThatDoesNotExist').and_return(true)
|
171
185
|
lambda { job.payload_object.perform }.should raise_error(Delayed::DeserializationError)
|
172
186
|
end
|
173
|
-
|
174
|
-
it "should be failed if it failed more than MAX_ATTEMPTS times and we don't want to destroy jobs" do
|
175
|
-
default = Delayed::Job.destroy_failed_jobs
|
176
|
-
Delayed::Job.destroy_failed_jobs = false
|
177
187
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
188
|
+
context "reschedule" do
|
189
|
+
before do
|
190
|
+
@job = Delayed::Job.create :payload_object => SimpleJob.new
|
191
|
+
end
|
182
192
|
|
183
|
-
|
184
|
-
|
193
|
+
context "and we want to destroy jobs" do
|
194
|
+
before do
|
195
|
+
Delayed::Worker.destroy_failed_jobs = true
|
196
|
+
end
|
185
197
|
|
186
|
-
|
187
|
-
|
188
|
-
|
198
|
+
it "should be destroyed if it failed more than MAX_ATTEMPTS times" do
|
199
|
+
@job.should_receive(:destroy)
|
200
|
+
Delayed::Job::MAX_ATTEMPTS.times { @job.reschedule 'FAIL' }
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should not be destroyed if failed fewer than MAX_ATTEMPTS times" do
|
204
|
+
@job.should_not_receive(:destroy)
|
205
|
+
(Delayed::Job::MAX_ATTEMPTS - 1).times { @job.reschedule 'FAIL' }
|
206
|
+
end
|
207
|
+
end
|
189
208
|
|
190
|
-
|
191
|
-
|
192
|
-
|
209
|
+
context "and we don't want to destroy jobs" do
|
210
|
+
before do
|
211
|
+
Delayed::Worker.destroy_failed_jobs = false
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should be failed if it failed more than MAX_ATTEMPTS times" do
|
215
|
+
@job.reload.failed_at.should == nil
|
216
|
+
Delayed::Job::MAX_ATTEMPTS.times { @job.reschedule 'FAIL' }
|
217
|
+
@job.reload.failed?.should == true
|
218
|
+
end
|
193
219
|
|
194
|
-
|
220
|
+
it "should not be failed if it failed fewer than MAX_ATTEMPTS times" do
|
221
|
+
(Delayed::Job::MAX_ATTEMPTS - 1).times { @job.reschedule 'FAIL' }
|
222
|
+
@job.reload.failed?.should == false
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
it "should fail after MAX_RUN_TIME" do
|
229
|
+
@job = Delayed::Job.create :payload_object => LongRunningJob.new
|
230
|
+
Delayed::Job.reserve_and_run_one_job(1.second)
|
231
|
+
@job.reload.last_error.should =~ /expired/
|
232
|
+
@job.attempts.should == 1
|
195
233
|
end
|
196
234
|
|
197
235
|
it "should never find failed jobs" do
|
@@ -202,7 +240,7 @@ describe Delayed::Job do
|
|
202
240
|
context "when another worker is already performing an task, it" do
|
203
241
|
|
204
242
|
before :each do
|
205
|
-
Delayed::
|
243
|
+
Delayed::Worker.name = 'worker1'
|
206
244
|
@job = Delayed::Job.create :payload_object => SimpleJob.new, :locked_by => 'worker1', :locked_at => Delayed::Job.db_time_now - 5.minutes
|
207
245
|
end
|
208
246
|
|
@@ -212,26 +250,27 @@ describe Delayed::Job do
|
|
212
250
|
|
213
251
|
it "should allow a second worker to get exclusive access if the timeout has passed" do
|
214
252
|
@job.lock_exclusively!(1.minute, 'worker2').should == true
|
215
|
-
end
|
216
|
-
|
253
|
+
end
|
254
|
+
|
217
255
|
it "should be able to get access to the task if it was started more then MAX_RUN_TIME ago" do
|
218
256
|
@job.locked_at = Delayed::Job.db_time_now - 5.hours
|
219
257
|
@job.save
|
220
258
|
|
221
259
|
@job.lock_exclusively!(4.hours, 'worker2').should == true
|
222
260
|
@job.reload
|
261
|
+
@job.locked?.should == true
|
223
262
|
@job.locked_by.should == 'worker2'
|
224
263
|
@job.locked_at.should > 1.minute.ago
|
225
264
|
end
|
226
265
|
|
227
266
|
it "should not be found by another worker" do
|
228
|
-
Delayed::
|
267
|
+
Delayed::Worker.name = 'worker2'
|
229
268
|
|
230
269
|
Delayed::Job.find_available(1, 6.minutes).length.should == 0
|
231
270
|
end
|
232
271
|
|
233
272
|
it "should be found by another worker if the time has expired" do
|
234
|
-
Delayed::
|
273
|
+
Delayed::Worker.name = 'worker2'
|
235
274
|
|
236
275
|
Delayed::Job.find_available(1, 4.minutes).length.should == 1
|
237
276
|
end
|
@@ -240,52 +279,71 @@ describe Delayed::Job do
|
|
240
279
|
@job.lock_exclusively! 5.minutes, 'worker1'
|
241
280
|
@job.lock_exclusively! 5.minutes, 'worker1'
|
242
281
|
@job.lock_exclusively! 5.minutes, 'worker1'
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
context "when another worker has worked on a task since the job was found to be available, it" do
|
286
|
+
|
287
|
+
before :each do
|
288
|
+
Delayed::Worker.name = 'worker1'
|
289
|
+
@job = Delayed::Job.create :payload_object => SimpleJob.new
|
290
|
+
@job_copy_for_worker_2 = Delayed::Job.find(@job.id)
|
291
|
+
end
|
292
|
+
|
293
|
+
it "should not allow a second worker to get exclusive access if already successfully processed by worker1" do
|
294
|
+
@job.delete
|
295
|
+
@job_copy_for_worker_2.lock_exclusively!(4.hours, 'worker2').should == false
|
296
|
+
end
|
297
|
+
|
298
|
+
it "should not allow a second worker to get exclusive access if failed to be processed by worker1 and run_at time is now in future (due to backing off behaviour)" do
|
299
|
+
@job.update_attributes(:attempts => 1, :run_at => Delayed::Job.db_time_now + 1.day)
|
300
|
+
@job_copy_for_worker_2.lock_exclusively!(4.hours, 'worker2').should == false
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
246
304
|
context "#name" do
|
247
305
|
it "should be the class name of the job that was enqueued" do
|
248
306
|
Delayed::Job.create(:payload_object => ErrorJob.new ).name.should == 'ErrorJob'
|
249
307
|
end
|
250
308
|
|
251
309
|
it "should be the method that will be called if its a performable method object" do
|
252
|
-
Delayed::Job.send_later(:
|
253
|
-
Delayed::Job.last.name.should == 'Delayed::Job.
|
310
|
+
Delayed::Job.send_later(:db_time_now)
|
311
|
+
Delayed::Job.last.name.should == 'Delayed::Job.db_time_now'
|
254
312
|
|
255
313
|
end
|
256
314
|
it "should be the instance method that will be called if its a performable method object" do
|
257
|
-
story = Story.create :text => "..."
|
258
|
-
|
315
|
+
story = Story.create :text => "..."
|
316
|
+
|
259
317
|
story.send_later(:save)
|
260
|
-
|
318
|
+
|
261
319
|
Delayed::Job.last.name.should == 'Story#save'
|
262
320
|
end
|
263
321
|
end
|
264
|
-
|
322
|
+
|
265
323
|
context "worker prioritization" do
|
266
|
-
|
324
|
+
|
267
325
|
before(:each) do
|
268
|
-
Delayed::
|
269
|
-
Delayed::
|
326
|
+
Delayed::Worker.max_priority = nil
|
327
|
+
Delayed::Worker.min_priority = nil
|
270
328
|
end
|
271
|
-
|
329
|
+
|
272
330
|
it "should only work_off jobs that are >= min_priority" do
|
273
|
-
Delayed::
|
274
|
-
Delayed::
|
331
|
+
Delayed::Worker.min_priority = -5
|
332
|
+
Delayed::Worker.max_priority = 5
|
275
333
|
SimpleJob.runs.should == 0
|
276
|
-
|
334
|
+
|
277
335
|
Delayed::Job.enqueue SimpleJob.new, -10
|
278
336
|
Delayed::Job.enqueue SimpleJob.new, 0
|
279
337
|
Delayed::Job.work_off
|
280
|
-
|
338
|
+
|
281
339
|
SimpleJob.runs.should == 1
|
282
340
|
end
|
283
|
-
|
341
|
+
|
284
342
|
it "should only work_off jobs that are <= max_priority" do
|
285
|
-
Delayed::
|
286
|
-
Delayed::
|
343
|
+
Delayed::Worker.min_priority = -5
|
344
|
+
Delayed::Worker.max_priority = 5
|
287
345
|
SimpleJob.runs.should == 0
|
288
|
-
|
346
|
+
|
289
347
|
Delayed::Job.enqueue SimpleJob.new, 10
|
290
348
|
Delayed::Job.enqueue SimpleJob.new, 0
|
291
349
|
|
@@ -293,44 +351,37 @@ describe Delayed::Job do
|
|
293
351
|
|
294
352
|
SimpleJob.runs.should == 1
|
295
353
|
end
|
296
|
-
|
354
|
+
|
297
355
|
it "should fetch jobs ordered by priority" do
|
298
356
|
NUM = 10
|
299
357
|
NUM.times { Delayed::Job.enqueue SimpleJob.new, rand(NUM) }
|
300
358
|
jobs = Delayed::Job.find_available(NUM)
|
301
|
-
|
302
|
-
jobs[0..-2].each_index do |i|
|
303
|
-
if (jobs[i].priority < jobs[i+1].priority)
|
304
|
-
ordered = false
|
305
|
-
break
|
306
|
-
end
|
307
|
-
end
|
308
|
-
ordered.should == true
|
359
|
+
jobs[0..-2].each_index { |i| (jobs[i].priority <= jobs[i+1].priority).should == true }
|
309
360
|
end
|
310
|
-
|
361
|
+
|
311
362
|
end
|
312
|
-
|
363
|
+
|
313
364
|
context "when pulling jobs off the queue for processing, it" do
|
314
365
|
before(:each) do
|
315
366
|
@job = Delayed::Job.create(
|
316
|
-
:payload_object => SimpleJob.new,
|
317
|
-
:locked_by => 'worker1',
|
367
|
+
:payload_object => SimpleJob.new,
|
368
|
+
:locked_by => 'worker1',
|
318
369
|
:locked_at => Delayed::Job.db_time_now - 5.minutes)
|
319
370
|
end
|
320
371
|
|
321
372
|
it "should leave the queue in a consistent state and not run the job if locking fails" do
|
322
|
-
SimpleJob.runs.should == 0
|
373
|
+
SimpleJob.runs.should == 0
|
323
374
|
@job.stub!(:lock_exclusively!).with(any_args).once.and_return(false)
|
324
375
|
Delayed::Job.should_receive(:find_available).once.and_return([@job])
|
325
376
|
Delayed::Job.work_off(1)
|
326
377
|
SimpleJob.runs.should == 0
|
327
378
|
end
|
328
|
-
|
379
|
+
|
329
380
|
end
|
330
|
-
|
381
|
+
|
331
382
|
context "while running alongside other workers that locked jobs, it" do
|
332
383
|
before(:each) do
|
333
|
-
Delayed::
|
384
|
+
Delayed::Worker.name = 'worker1'
|
334
385
|
Delayed::Job.create(:payload_object => SimpleJob.new, :locked_by => 'worker1', :locked_at => (Delayed::Job.db_time_now - 1.minutes))
|
335
386
|
Delayed::Job.create(:payload_object => SimpleJob.new, :locked_by => 'worker2', :locked_at => (Delayed::Job.db_time_now - 1.minutes))
|
336
387
|
Delayed::Job.create(:payload_object => SimpleJob.new)
|
@@ -338,14 +389,14 @@ describe Delayed::Job do
|
|
338
389
|
end
|
339
390
|
|
340
391
|
it "should ingore locked jobs from other workers" do
|
341
|
-
Delayed::
|
392
|
+
Delayed::Worker.name = 'worker3'
|
342
393
|
SimpleJob.runs.should == 0
|
343
394
|
Delayed::Job.work_off
|
344
395
|
SimpleJob.runs.should == 1 # runs the one open job
|
345
396
|
end
|
346
397
|
|
347
398
|
it "should find our own jobs regardless of locks" do
|
348
|
-
Delayed::
|
399
|
+
Delayed::Worker.name = 'worker1'
|
349
400
|
SimpleJob.runs.should == 0
|
350
401
|
Delayed::Job.work_off
|
351
402
|
SimpleJob.runs.should == 3 # runs open job plus worker1 jobs that were already locked
|
@@ -354,7 +405,7 @@ describe Delayed::Job do
|
|
354
405
|
|
355
406
|
context "while running with locked and expired jobs, it" do
|
356
407
|
before(:each) do
|
357
|
-
Delayed::
|
408
|
+
Delayed::Worker.name = 'worker1'
|
358
409
|
exp_time = Delayed::Job.db_time_now - (1.minutes + Delayed::Job::MAX_RUN_TIME)
|
359
410
|
Delayed::Job.create(:payload_object => SimpleJob.new, :locked_by => 'worker1', :locked_at => exp_time)
|
360
411
|
Delayed::Job.create(:payload_object => SimpleJob.new, :locked_by => 'worker2', :locked_at => (Delayed::Job.db_time_now - 1.minutes))
|
@@ -363,14 +414,14 @@ describe Delayed::Job do
|
|
363
414
|
end
|
364
415
|
|
365
416
|
it "should only find unlocked and expired jobs" do
|
366
|
-
Delayed::
|
417
|
+
Delayed::Worker.name = 'worker3'
|
367
418
|
SimpleJob.runs.should == 0
|
368
419
|
Delayed::Job.work_off
|
369
420
|
SimpleJob.runs.should == 2 # runs the one open job and one expired job
|
370
421
|
end
|
371
422
|
|
372
423
|
it "should ignore locks when finding our own jobs" do
|
373
|
-
Delayed::
|
424
|
+
Delayed::Worker.name = 'worker1'
|
374
425
|
SimpleJob.runs.should == 0
|
375
426
|
Delayed::Job.work_off
|
376
427
|
SimpleJob.runs.should == 3 # runs open job plus worker1 jobs
|
@@ -382,9 +433,9 @@ describe Delayed::Job do
|
|
382
433
|
context "db_time_now" do
|
383
434
|
it "should return time in current time zone if set" do
|
384
435
|
Time.zone = 'Eastern Time (US & Canada)'
|
385
|
-
Delayed::Job.db_time_now.zone.should match
|
436
|
+
Delayed::Job.db_time_now.zone.should match(/EST|EDT/)
|
386
437
|
end
|
387
|
-
|
438
|
+
|
388
439
|
it "should return UTC time if that is the AR default" do
|
389
440
|
Time.zone = nil
|
390
441
|
ActiveRecord::Base.default_timezone = :utc
|
@@ -397,5 +448,5 @@ describe Delayed::Job do
|
|
397
448
|
Delayed::Job.db_time_now.zone.should_not == 'UTC'
|
398
449
|
end
|
399
450
|
end
|
400
|
-
|
451
|
+
|
401
452
|
end
|