Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 346 lines (260 sloc) 11.825 kB
75b49dc @tobi Initial extraction
authored
1 require File.dirname(__FILE__) + '/database'
2
3 class SimpleJob
0581928 @technoweenie remove excess whitespace
technoweenie authored
4 cattr_accessor :runs; self.runs = 0
75b49dc @tobi Initial extraction
authored
5 def perform; @@runs += 1; end
0581928 @technoweenie remove excess whitespace
technoweenie authored
6 end
75b49dc @tobi Initial extraction
authored
7
8 class ErrorJob
0581928 @technoweenie remove excess whitespace
technoweenie authored
9 cattr_accessor :runs; self.runs = 0
10 def perform; raise 'did not work'; end
4fd41a9 @tobi Fixed a bug that prevented jobs in sub modules from being serialized …
authored
11 end
12
13 module M
14 class ModuleJob
15 cattr_accessor :runs; self.runs = 0
16 def perform; @@runs += 1; end
17 end
18
75b49dc @tobi Initial extraction
authored
19 end
20
21 describe Delayed::Job do
249c5a9 @tobi Added min/max priority levels for workers. This allows you to have de…
authored
22 before do
23 Delayed::Job.max_priority = nil
24 Delayed::Job.min_priority = nil
25
26 Delayed::Job.delete_all
27 end
28
29 before(:each) do
30 SimpleJob.runs = 0
31 end
75b49dc @tobi Initial extraction
authored
32
41552cf @mocoso Added support to Delayed::Job#enqueue for setting jobs to run after a…
mocoso authored
33 it "should set run_at automatically if not set" do
b9dc92b @tobi Don't save jobs with empty handlers
authored
34 Delayed::Job.create(:payload_object => ErrorJob.new ).run_at.should_not == nil
0581928 @technoweenie remove excess whitespace
technoweenie authored
35 end
75b49dc @tobi Initial extraction
authored
36
41552cf @mocoso Added support to Delayed::Job#enqueue for setting jobs to run after a…
mocoso authored
37 it "should not set run_at automatically if already set" do
38 later = 5.minutes.from_now
39 Delayed::Job.create(:payload_object => ErrorJob.new, :run_at => later).run_at.should == later
40 end
41
75b49dc @tobi Initial extraction
authored
42 it "should raise ArgumentError when handler doesn't respond_to :perform" do
43 lambda { Delayed::Job.enqueue(Object.new) }.should raise_error(ArgumentError)
44 end
0581928 @technoweenie remove excess whitespace
technoweenie authored
45
75b49dc @tobi Initial extraction
authored
46 it "should increase count after enqueuing items" do
0581928 @technoweenie remove excess whitespace
technoweenie authored
47 Delayed::Job.enqueue SimpleJob.new
75b49dc @tobi Initial extraction
authored
48 Delayed::Job.count.should == 1
49 end
0581928 @technoweenie remove excess whitespace
technoweenie authored
50
41552cf @mocoso Added support to Delayed::Job#enqueue for setting jobs to run after a…
mocoso authored
51 it "should be able to set priority when enqueuing items" do
52 Delayed::Job.enqueue SimpleJob.new, 5
53 Delayed::Job.first.priority.should == 5
54 end
55
56 it "should be able to set run_at when enqueuing items" do
d5cf56f fixing the Time.zone nil error, updating all specs to comply with the…
Tim Matheson authored
57 later = (Delayed::Job.db_time_now+5.minutes)
41552cf @mocoso Added support to Delayed::Job#enqueue for setting jobs to run after a…
mocoso authored
58 Delayed::Job.enqueue SimpleJob.new, 5, later
59
60 # use be close rather than equal to because millisecond values cn be lost in DB round trip
61 Delayed::Job.first.run_at.should be_close(later, 1)
62 end
63
0581928 @technoweenie remove excess whitespace
technoweenie authored
64 it "should call perform on jobs when running work_off" do
75b49dc @tobi Initial extraction
authored
65 SimpleJob.runs.should == 0
0581928 @technoweenie remove excess whitespace
technoweenie authored
66
67 Delayed::Job.enqueue SimpleJob.new
8ec934e @tobi Removed the global lock. Jobs can now processed in parallel by runnin…
authored
68 Delayed::Job.work_off
0581928 @technoweenie remove excess whitespace
technoweenie authored
69
70 SimpleJob.runs.should == 1
71 end
4fd41a9 @tobi Fixed a bug that prevented jobs in sub modules from being serialized …
authored
72
73
89c3a0b @dhh Added eval jobs as a low ceremony way of declaring jobs without an ex…
dhh authored
74 it "should work with eval jobs" do
75 $eval_job_ran = false
76
77 Delayed::Job.enqueue do <<-JOB
78 $eval_job_ran = true
79 JOB
80 end
81
82 Delayed::Job.work_off
83
84 $eval_job_ran.should == true
85 end
86
4fd41a9 @tobi Fixed a bug that prevented jobs in sub modules from being serialized …
authored
87 it "should work with jobs in modules" do
88 M::ModuleJob.runs.should == 0
89
90 Delayed::Job.enqueue M::ModuleJob.new
91 Delayed::Job.work_off
a137def @defunkt Changes extracted from GitHub:
defunkt authored
92
4fd41a9 @tobi Fixed a bug that prevented jobs in sub modules from being serialized …
authored
93 M::ModuleJob.runs.should == 1
94 end
95
a137def @defunkt Changes extracted from GitHub:
defunkt authored
96 it "should re-schedule by about 1 second at first and increment this more and more minutes when it fails to execute properly" do
0581928 @technoweenie remove excess whitespace
technoweenie authored
97 Delayed::Job.enqueue ErrorJob.new
0c084e5 @technoweenie merge
technoweenie authored
98 Delayed::Job.work_off(1)
8ec934e @tobi Removed the global lock. Jobs can now processed in parallel by runnin…
authored
99
75b49dc @tobi Initial extraction
authored
100 job = Delayed::Job.find(:first)
a137def @defunkt Changes extracted from GitHub:
defunkt authored
101
102 job.last_error.should =~ /did not work/
103 job.last_error.should =~ /job_spec.rb:10:in `perform'/
75b49dc @tobi Initial extraction
authored
104 job.attempts.should == 1
0c084e5 @technoweenie merge
technoweenie authored
105
106 job.run_at.should > Delayed::Job.db_time_now - 10.minutes
107 job.run_at.should < Delayed::Job.db_time_now + 10.minutes
0581928 @technoweenie remove excess whitespace
technoweenie authored
108 end
109
75b49dc @tobi Initial extraction
authored
110 it "should raise an DeserializationError when the job class is totally unknown" do
111
a137def @defunkt Changes extracted from GitHub:
defunkt authored
112 job = Delayed::Job.new
75b49dc @tobi Initial extraction
authored
113 job['handler'] = "--- !ruby/object:JobThatDoesNotExist {}"
114
0581928 @technoweenie remove excess whitespace
technoweenie authored
115 lambda { job.payload_object.perform }.should raise_error(Delayed::DeserializationError)
75b49dc @tobi Initial extraction
authored
116 end
117
118 it "should try to load the class when it is unknown at the time of the deserialization" do
0581928 @technoweenie remove excess whitespace
technoweenie authored
119 job = Delayed::Job.new
75b49dc @tobi Initial extraction
authored
120 job['handler'] = "--- !ruby/object:JobThatDoesNotExist {}"
121
122 job.should_receive(:attempt_to_load).with('JobThatDoesNotExist').and_return(true)
0581928 @technoweenie remove excess whitespace
technoweenie authored
123
124 lambda { job.payload_object.perform }.should raise_error(Delayed::DeserializationError)
125 end
126
75b49dc @tobi Initial extraction
authored
127 it "should try include the namespace when loading unknown objects" do
0581928 @technoweenie remove excess whitespace
technoweenie authored
128 job = Delayed::Job.new
75b49dc @tobi Initial extraction
authored
129 job['handler'] = "--- !ruby/object:Delayed::JobThatDoesNotExist {}"
0581928 @technoweenie remove excess whitespace
technoweenie authored
130 job.should_receive(:attempt_to_load).with('Delayed::JobThatDoesNotExist').and_return(true)
131 lambda { job.payload_object.perform }.should raise_error(Delayed::DeserializationError)
132 end
133
75b49dc @tobi Initial extraction
authored
134 it "should also try to load structs when they are unknown (raises TypeError)" do
0581928 @technoweenie remove excess whitespace
technoweenie authored
135 job = Delayed::Job.new
75b49dc @tobi Initial extraction
authored
136 job['handler'] = "--- !ruby/struct:JobThatDoesNotExist {}"
137
138 job.should_receive(:attempt_to_load).with('JobThatDoesNotExist').and_return(true)
0581928 @technoweenie remove excess whitespace
technoweenie authored
139
140 lambda { job.payload_object.perform }.should raise_error(Delayed::DeserializationError)
141 end
142
75b49dc @tobi Initial extraction
authored
143 it "should try include the namespace when loading unknown structs" do
0581928 @technoweenie remove excess whitespace
technoweenie authored
144 job = Delayed::Job.new
75b49dc @tobi Initial extraction
authored
145 job['handler'] = "--- !ruby/struct:Delayed::JobThatDoesNotExist {}"
0c084e5 @technoweenie merge
technoweenie authored
146
0581928 @technoweenie remove excess whitespace
technoweenie authored
147 job.should_receive(:attempt_to_load).with('Delayed::JobThatDoesNotExist').and_return(true)
148 lambda { job.payload_object.perform }.should raise_error(Delayed::DeserializationError)
a137def @defunkt Changes extracted from GitHub:
defunkt authored
149 end
41552cf @mocoso Added support to Delayed::Job#enqueue for setting jobs to run after a…
mocoso authored
150
a693dc9 @defunkt test both sides of destroy_jobs behavior
defunkt authored
151 it "should be failed if it failed more than MAX_ATTEMPTS times and we don't want to destroy jobs" do
757f585 @defunkt add documentation and rename destroy_jobs to the more descriptive des…
defunkt authored
152 default = Delayed::Job.destroy_failed_jobs
153 Delayed::Job.destroy_failed_jobs = false
b53ac48 @defunkt whether or not jobs should be destroyed is now configurable (old jobs…
defunkt authored
154
a6e374e @tobi Fixed a nasty bug that allowed several job runners to get the exclusi…
authored
155 @job = Delayed::Job.create :payload_object => SimpleJob.new, :attempts => 50
b53ac48 @defunkt whether or not jobs should be destroyed is now configurable (old jobs…
defunkt authored
156 @job.reload.failed_at.should == nil
0c084e5 @technoweenie merge
technoweenie authored
157 @job.reschedule 'FAIL'
b53ac48 @defunkt whether or not jobs should be destroyed is now configurable (old jobs…
defunkt authored
158 @job.reload.failed_at.should_not == nil
159
757f585 @defunkt add documentation and rename destroy_jobs to the more descriptive des…
defunkt authored
160 Delayed::Job.destroy_failed_jobs = default
a137def @defunkt Changes extracted from GitHub:
defunkt authored
161 end
162
a693dc9 @defunkt test both sides of destroy_jobs behavior
defunkt authored
163 it "should be destroyed if it failed more than MAX_ATTEMPTS times and we want to destroy jobs" do
757f585 @defunkt add documentation and rename destroy_jobs to the more descriptive des…
defunkt authored
164 default = Delayed::Job.destroy_failed_jobs
165 Delayed::Job.destroy_failed_jobs = true
a6e374e @tobi Fixed a nasty bug that allowed several job runners to get the exclusi…
authored
166
167 @job = Delayed::Job.create :payload_object => SimpleJob.new, :attempts => 50
0c084e5 @technoweenie merge
technoweenie authored
168 @job.should_receive(:destroy)
169 @job.reschedule 'FAIL'
a693dc9 @defunkt test both sides of destroy_jobs behavior
defunkt authored
170
757f585 @defunkt add documentation and rename destroy_jobs to the more descriptive des…
defunkt authored
171 Delayed::Job.destroy_failed_jobs = default
a693dc9 @defunkt test both sides of destroy_jobs behavior
defunkt authored
172 end
173
a137def @defunkt Changes extracted from GitHub:
defunkt authored
174 it "should never find failed jobs" do
d5cf56f fixing the Time.zone nil error, updating all specs to comply with the…
Tim Matheson authored
175 @job = Delayed::Job.create :payload_object => SimpleJob.new, :attempts => 50, :failed_at => Delayed::Job.db_time_now
a137def @defunkt Changes extracted from GitHub:
defunkt authored
176 Delayed::Job.find_available(1).length.should == 0
0581928 @technoweenie remove excess whitespace
technoweenie authored
177 end
178
be23e26 @rares fixing spec that was returning a false positive. adding spec for conc…
rares authored
179 context "when another worker is already performing an task, it" do
0581928 @technoweenie remove excess whitespace
technoweenie authored
180
8ec934e @tobi Removed the global lock. Jobs can now processed in parallel by runnin…
authored
181 before :each do
182 Delayed::Job.worker_name = 'worker1'
a6e374e @tobi Fixed a nasty bug that allowed several job runners to get the exclusi…
authored
183 @job = Delayed::Job.create :payload_object => SimpleJob.new, :locked_by => 'worker1', :locked_at => Delayed::Job.db_time_now - 5.minutes
8ec934e @tobi Removed the global lock. Jobs can now processed in parallel by runnin…
authored
184 end
0581928 @technoweenie remove excess whitespace
technoweenie authored
185
186 it "should not allow a second worker to get exclusive access" do
266fc15 @dmag refactor job object
dmag authored
187 @job.lock_exclusively!(4.hours, 'worker2').should == false
be23e26 @rares fixing spec that was returning a false positive. adding spec for conc…
rares authored
188 end
a6e374e @tobi Fixed a nasty bug that allowed several job runners to get the exclusi…
authored
189
be23e26 @rares fixing spec that was returning a false positive. adding spec for conc…
rares authored
190 it "should allow a second worker to get exclusive access if the timeout has passed" do
266fc15 @dmag refactor job object
dmag authored
191 @job.lock_exclusively!(1.minute, 'worker2').should == true
a6e374e @tobi Fixed a nasty bug that allowed several job runners to get the exclusi…
authored
192 end
5998931 Renamed locked_until to locked_at. We now store when we start a given…
Tobias Lütke authored
193
0581928 @technoweenie remove excess whitespace
technoweenie authored
194 it "should be able to get access to the task if it was started more then max_age ago" do
5998931 Renamed locked_until to locked_at. We now store when we start a given…
Tobias Lütke authored
195 @job.locked_at = 5.hours.ago
196 @job.save
197
0581928 @technoweenie remove excess whitespace
technoweenie authored
198 @job.lock_exclusively! 4.hours, 'worker2'
5998931 Renamed locked_until to locked_at. We now store when we start a given…
Tobias Lütke authored
199 @job.reload
200 @job.locked_by.should == 'worker2'
201 @job.locked_at.should > 1.minute.ago
202 end
8ec934e @tobi Removed the global lock. Jobs can now processed in parallel by runnin…
authored
203
7274142 @eric Job#find_available does not return running jobs
eric authored
204 it "should not be found by another worker" do
205 Delayed::Job.worker_name = 'worker2'
75b49dc @tobi Initial extraction
authored
206
7274142 @eric Job#find_available does not return running jobs
eric authored
207 Delayed::Job.find_available(1, 6.minutes).length.should == 0
208 end
a137def @defunkt Changes extracted from GitHub:
defunkt authored
209
7274142 @eric Job#find_available does not return running jobs
eric authored
210 it "should be found by another worker if the time has expired" do
211 Delayed::Job.worker_name = 'worker2'
a137def @defunkt Changes extracted from GitHub:
defunkt authored
212
7274142 @eric Job#find_available does not return running jobs
eric authored
213 Delayed::Job.find_available(1, 4.minutes).length.should == 1
214 end
8ec934e @tobi Removed the global lock. Jobs can now processed in parallel by runnin…
authored
215
a137def @defunkt Changes extracted from GitHub:
defunkt authored
216 it "should be able to get exclusive access again when the worker name is the same" do
a6e374e @tobi Fixed a nasty bug that allowed several job runners to get the exclusi…
authored
217 @job.lock_exclusively! 5.minutes, 'worker1'
0c084e5 @technoweenie merge
technoweenie authored
218 @job.lock_exclusively! 5.minutes, 'worker1'
219 @job.lock_exclusively! 5.minutes, 'worker1'
8ec934e @tobi Removed the global lock. Jobs can now processed in parallel by runnin…
authored
220 end
916e9f2 @tobi Slightly more useful Job#name method that can easily be used for insp…
authored
221 end
222
223 context "#name" do
224 it "should be the class name of the job that was enqueued" do
225 Delayed::Job.create(:payload_object => ErrorJob.new ).name.should == 'ErrorJob'
226 end
227
228 it "should be the method that will be called if its a performable method object" do
229 Delayed::Job.send_later(:clear_locks!)
4fd41a9 @tobi Fixed a bug that prevented jobs in sub modules from being serialized …
authored
230 Delayed::Job.last.name.should == 'Delayed::Job.clear_locks!'
231
232 end
233 it "should be the instance method that will be called if its a performable method object" do
234 story = Story.create :text => "..."
235
236 story.send_later(:save)
237
238 Delayed::Job.last.name.should == 'Story#save'
916e9f2 @tobi Slightly more useful Job#name method that can easily be used for insp…
authored
239 end
240 end
249c5a9 @tobi Added min/max priority levels for workers. This allows you to have de…
authored
241
242 context "worker prioritization" do
243
244 before(:each) do
245 Delayed::Job.max_priority = nil
246 Delayed::Job.min_priority = nil
247 end
248
249 it "should only work_off jobs that are >= min_priority" do
250 Delayed::Job.min_priority = -5
251 Delayed::Job.max_priority = 5
252 SimpleJob.runs.should == 0
253
254 Delayed::Job.enqueue SimpleJob.new, -10
255 Delayed::Job.enqueue SimpleJob.new, 0
256 Delayed::Job.work_off
257
258 SimpleJob.runs.should == 1
259 end
260
261 it "should only work_off jobs that are <= max_priority" do
262 Delayed::Job.min_priority = -5
263 Delayed::Job.max_priority = 5
264 SimpleJob.runs.should == 0
265
266 Delayed::Job.enqueue SimpleJob.new, 10
267 Delayed::Job.enqueue SimpleJob.new, 0
268
269 Delayed::Job.work_off
270
271 SimpleJob.runs.should == 1
272 end
273
8ec934e @tobi Removed the global lock. Jobs can now processed in parallel by runnin…
authored
274 end
249c5a9 @tobi Added min/max priority levels for workers. This allows you to have de…
authored
275
5855847 @rares removing worthless spec that duplicated a previous one.
rares authored
276 context "when pulling jobs off the queue for processing, it" do
4b9b079 @rares active record changes and fixing some small bugs.
rares authored
277 before(:each) do
5e8745c @rares more specs around poping jobs off the queue.
rares authored
278 @job = Delayed::Job.create(
279 :payload_object => SimpleJob.new,
280 :locked_by => 'worker1',
281 :locked_at => Delayed::Job.db_time_now - 5.minutes)
4b9b079 @rares active record changes and fixing some small bugs.
rares authored
282 end
be23e26 @rares fixing spec that was returning a false positive. adding spec for conc…
rares authored
283
5855847 @rares removing worthless spec that duplicated a previous one.
rares authored
284 it "should leave the queue in a consistent state and not run the job if locking fails" do
5e8745c @rares more specs around poping jobs off the queue.
rares authored
285 SimpleJob.runs.should == 0
266fc15 @dmag refactor job object
dmag authored
286 @job.stub!(:lock_exclusively!).with(any_args).once.and_return(false)
149f45f @rares adding new un-finished specs
rares authored
287 Delayed::Job.should_receive(:find_available).once.and_return([@job])
5e8745c @rares more specs around poping jobs off the queue.
rares authored
288 Delayed::Job.work_off(1)
289 SimpleJob.runs.should == 0
4b9b079 @rares active record changes and fixing some small bugs.
rares authored
290 end
291
292 end
293
266fc15 @dmag refactor job object
dmag authored
294 context "while running alongside other workers that locked jobs, it" do
149f45f @rares adding new un-finished specs
rares authored
295 before(:each) do
be23e26 @rares fixing spec that was returning a false positive. adding spec for conc…
rares authored
296 Delayed::Job.worker_name = 'worker1'
266fc15 @dmag refactor job object
dmag authored
297 Delayed::Job.create(:payload_object => SimpleJob.new, :locked_by => 'worker1', :locked_at => (Delayed::Job.db_time_now - 1.minutes))
298 Delayed::Job.create(:payload_object => SimpleJob.new, :locked_by => 'worker2', :locked_at => (Delayed::Job.db_time_now - 1.minutes))
299 Delayed::Job.create(:payload_object => SimpleJob.new)
300 Delayed::Job.create(:payload_object => SimpleJob.new, :locked_by => 'worker1', :locked_at => (Delayed::Job.db_time_now - 1.minutes))
149f45f @rares adding new un-finished specs
rares authored
301 end
266fc15 @dmag refactor job object
dmag authored
302
303 it "should ingore locked jobs from other workers" do
304 Delayed::Job.worker_name = 'worker3'
305 SimpleJob.runs.should == 0
306 Delayed::Job.work_off
307 SimpleJob.runs.should == 1 # runs the one open job
149f45f @rares adding new un-finished specs
rares authored
308 end
266fc15 @dmag refactor job object
dmag authored
309
310 it "should find our own jobs regardless of locks" do
311 Delayed::Job.worker_name = 'worker1'
312 SimpleJob.runs.should == 0
313 Delayed::Job.work_off
314 SimpleJob.runs.should == 3 # runs open job plus worker1 jobs that were already locked
315 end
316 end
317
318 context "while running with locked and expired jobs, it" do
319 before(:each) do
320 Delayed::Job.worker_name = 'worker1'
321 exp_time = Delayed::Job.db_time_now - (1.minutes + Delayed::Job::MAX_RUN_TIME)
322 Delayed::Job.create(:payload_object => SimpleJob.new, :locked_by => 'worker1', :locked_at => exp_time)
323 Delayed::Job.create(:payload_object => SimpleJob.new, :locked_by => 'worker2', :locked_at => (Delayed::Job.db_time_now - 1.minutes))
324 Delayed::Job.create(:payload_object => SimpleJob.new)
325 Delayed::Job.create(:payload_object => SimpleJob.new, :locked_by => 'worker1', :locked_at => (Delayed::Job.db_time_now - 1.minutes))
326 end
327
328 it "should only find unlocked and expired jobs" do
329 Delayed::Job.worker_name = 'worker3'
330 SimpleJob.runs.should == 0
331 Delayed::Job.work_off
332 SimpleJob.runs.should == 2 # runs the one open job and one expired job
333 end
334
335 it "should ignore locks when finding our own jobs" do
336 Delayed::Job.worker_name = 'worker1'
337 SimpleJob.runs.should == 0
338 Delayed::Job.work_off
339 SimpleJob.runs.should == 3 # runs open job plus worker1 jobs
340 # This is useful in the case of a crash/restart on worker1, but make sure multiple workers on the same host have unique names!
341 end
342
149f45f @rares adding new un-finished specs
rares authored
343 end
344
a137def @defunkt Changes extracted from GitHub:
defunkt authored
345 end
Something went wrong with that request. Please try again.