delayed_job_unique_key 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.textile +246 -0
  3. data/contrib/delayed_job.monitrc +14 -0
  4. data/contrib/delayed_job_multiple.monitrc +23 -0
  5. data/lib/delayed/backend/base.rb +152 -0
  6. data/lib/delayed/backend/shared_spec.rb +566 -0
  7. data/lib/delayed/command.rb +101 -0
  8. data/lib/delayed/deserialization_error.rb +4 -0
  9. data/lib/delayed/lifecycle.rb +84 -0
  10. data/lib/delayed/message_sending.rb +54 -0
  11. data/lib/delayed/performable_mailer.rb +21 -0
  12. data/lib/delayed/performable_method.rb +33 -0
  13. data/lib/delayed/plugin.rb +15 -0
  14. data/lib/delayed/plugins/clear_locks.rb +15 -0
  15. data/lib/delayed/psych_ext.rb +75 -0
  16. data/lib/delayed/railtie.rb +16 -0
  17. data/lib/delayed/recipes.rb +50 -0
  18. data/lib/delayed/serialization/active_record.rb +19 -0
  19. data/lib/delayed/syck_ext.rb +34 -0
  20. data/lib/delayed/tasks.rb +11 -0
  21. data/lib/delayed/worker.rb +222 -0
  22. data/lib/delayed/yaml_ext.rb +10 -0
  23. data/lib/delayed_job.rb +22 -0
  24. data/lib/generators/delayed_job/delayed_job_generator.rb +11 -0
  25. data/lib/generators/delayed_job/templates/script +5 -0
  26. data/recipes/delayed_job.rb +1 -0
  27. data/spec/autoloaded/clazz.rb +7 -0
  28. data/spec/autoloaded/instance_clazz.rb +6 -0
  29. data/spec/autoloaded/instance_struct.rb +6 -0
  30. data/spec/autoloaded/struct.rb +7 -0
  31. data/spec/delayed/backend/test.rb +113 -0
  32. data/spec/delayed/serialization/test.rb +0 -0
  33. data/spec/fixtures/bad_alias.yml +1 -0
  34. data/spec/lifecycle_spec.rb +107 -0
  35. data/spec/message_sending_spec.rb +116 -0
  36. data/spec/performable_mailer_spec.rb +46 -0
  37. data/spec/performable_method_spec.rb +89 -0
  38. data/spec/sample_jobs.rb +75 -0
  39. data/spec/spec_helper.rb +45 -0
  40. data/spec/test_backend_spec.rb +13 -0
  41. data/spec/worker_spec.rb +19 -0
  42. data/spec/yaml_ext_spec.rb +41 -0
  43. metadata +197 -0
@@ -0,0 +1,566 @@
1
+ require File.expand_path('../../../../spec/sample_jobs', __FILE__)
2
+
3
+ require 'active_support/core_ext'
4
+
5
+ shared_examples_for 'a delayed_job backend' do
6
+ let(:worker) { Delayed::Worker.new }
7
+
8
+ def create_job(opts = {})
9
+ described_class.create(opts.merge(:payload_object => SimpleJob.new))
10
+ end
11
+
12
+ before do
13
+ Delayed::Worker.max_priority = nil
14
+ Delayed::Worker.min_priority = nil
15
+ Delayed::Worker.default_priority = 99
16
+ Delayed::Worker.delay_jobs = true
17
+ SimpleJob.runs = 0
18
+ described_class.delete_all
19
+ end
20
+
21
+ it "should set run_at automatically if not set" do
22
+ described_class.create(:payload_object => ErrorJob.new ).run_at.should_not be_nil
23
+ end
24
+
25
+ it "should not set run_at automatically if already set" do
26
+ later = described_class.db_time_now + 5.minutes
27
+ job = described_class.create(:payload_object => ErrorJob.new, :run_at => later)
28
+ job.run_at.should be_within(1).of(later)
29
+ end
30
+
31
+ describe "#reload" do
32
+ it 'should cause the payload to be reloaded' do
33
+ job = described_class.enqueue :payload_object => SimpleJob.new
34
+ job.payload_object.object_id.should_not == job.reload.payload_object.object_id
35
+ end
36
+ end
37
+
38
+ describe "enqueue" do
39
+ context "with a hash" do
40
+ it "should raise ArgumentError when handler doesn't respond_to :perform" do
41
+ lambda { described_class.enqueue(:payload_object => Object.new) }.should raise_error(ArgumentError)
42
+ end
43
+
44
+ it "should be able to set priority" do
45
+ job = described_class.enqueue :payload_object => SimpleJob.new, :priority => 5
46
+ job.priority.should == 5
47
+ end
48
+
49
+ it "should use default priority" do
50
+ job = described_class.enqueue :payload_object => SimpleJob.new
51
+ job.priority.should == 99
52
+ end
53
+
54
+ it "should be able to set run_at" do
55
+ later = described_class.db_time_now + 5.minutes
56
+ job = described_class.enqueue :payload_object => SimpleJob.new, :run_at => later
57
+ job.run_at.should be_within(1).of(later)
58
+ end
59
+
60
+ it "should be able to set queue" do
61
+ job = described_class.enqueue :payload_object => SimpleJob.new, :queue => 'tracking'
62
+ job.queue.should == 'tracking'
63
+ end
64
+ end
65
+
66
+ context "with multiple arguments" do
67
+ it "should raise ArgumentError when handler doesn't respond_to :perform" do
68
+ lambda { described_class.enqueue(Object.new) }.should raise_error(ArgumentError)
69
+ end
70
+
71
+ it "should increase count after enqueuing items" do
72
+ described_class.enqueue SimpleJob.new
73
+ described_class.count.should == 1
74
+ end
75
+
76
+ it "should be able to set priority [DEPRECATED]" do
77
+ silence_warnings do
78
+ job = described_class.enqueue SimpleJob.new, 5
79
+ job.priority.should == 5
80
+ end
81
+ end
82
+
83
+ it "should use default priority when it is not set" do
84
+ @job = described_class.enqueue SimpleJob.new
85
+ @job.priority.should == 99
86
+ end
87
+
88
+ it "should be able to set run_at [DEPRECATED]" do
89
+ silence_warnings do
90
+ later = described_class.db_time_now + 5.minutes
91
+ @job = described_class.enqueue SimpleJob.new, 5, later
92
+ @job.run_at.should be_within(1).of(later)
93
+ end
94
+ end
95
+
96
+ it "should work with jobs in modules" do
97
+ M::ModuleJob.runs = 0
98
+ job = described_class.enqueue M::ModuleJob.new
99
+ lambda { job.invoke_job }.should change { M::ModuleJob.runs }.from(0).to(1)
100
+ end
101
+ end
102
+
103
+ context "with delay_jobs = false" do
104
+ before(:each) do
105
+ Delayed::Worker.delay_jobs = false
106
+ end
107
+
108
+ it "should not increase count after enqueuing items" do
109
+ described_class.enqueue SimpleJob.new
110
+ described_class.count.should == 0
111
+ end
112
+
113
+ it 'should invoke the enqueued job' do
114
+ job = SimpleJob.new
115
+ job.should_receive(:perform)
116
+ described_class.enqueue job
117
+ end
118
+
119
+ it 'should return a job, not the result of invocation' do
120
+ described_class.enqueue(SimpleJob.new).should be_instance_of(described_class)
121
+ end
122
+ end
123
+ end
124
+
125
+ describe "callbacks" do
126
+ before(:each) do
127
+ CallbackJob.messages = []
128
+ end
129
+
130
+ %w(before success after).each do |callback|
131
+ it "should call #{callback} with job" do
132
+ job = described_class.enqueue(CallbackJob.new)
133
+ job.payload_object.should_receive(callback).with(job)
134
+ job.invoke_job
135
+ end
136
+ end
137
+
138
+ it "should call before and after callbacks" do
139
+ job = described_class.enqueue(CallbackJob.new)
140
+ CallbackJob.messages.should == ["enqueue"]
141
+ job.invoke_job
142
+ CallbackJob.messages.should == ["enqueue", "before", "perform", "success", "after"]
143
+ end
144
+
145
+ it "should call the after callback with an error" do
146
+ job = described_class.enqueue(CallbackJob.new)
147
+ job.payload_object.should_receive(:perform).and_raise(RuntimeError.new("fail"))
148
+
149
+ lambda { job.invoke_job }.should raise_error
150
+ CallbackJob.messages.should == ["enqueue", "before", "error: RuntimeError", "after"]
151
+ end
152
+
153
+ it "should call error when before raises an error" do
154
+ job = described_class.enqueue(CallbackJob.new)
155
+ job.payload_object.should_receive(:before).and_raise(RuntimeError.new("fail"))
156
+ lambda { job.invoke_job }.should raise_error(RuntimeError)
157
+ CallbackJob.messages.should == ["enqueue", "error: RuntimeError", "after"]
158
+ end
159
+ end
160
+
161
+ describe "payload_object" do
162
+ it "should raise a DeserializationError when the job class is totally unknown" do
163
+ job = described_class.new :handler => "--- !ruby/object:JobThatDoesNotExist {}"
164
+ lambda { job.payload_object }.should raise_error(Delayed::DeserializationError)
165
+ end
166
+
167
+ it "should raise a DeserializationError when the job struct is totally unknown" do
168
+ job = described_class.new :handler => "--- !ruby/struct:StructThatDoesNotExist {}"
169
+ lambda { job.payload_object }.should raise_error(Delayed::DeserializationError)
170
+ end
171
+
172
+ it "should raise a DeserializationError when the YAML.load raises argument error" do
173
+ job = described_class.new :handler => "--- !ruby/struct:GoingToRaiseArgError {}"
174
+ YAML.should_receive(:load).and_raise(ArgumentError)
175
+ lambda { job.payload_object }.should raise_error(Delayed::DeserializationError)
176
+ end
177
+ end
178
+
179
+ describe "reserve" do
180
+ before do
181
+ Delayed::Worker.max_run_time = 2.minutes
182
+ end
183
+
184
+ it "should not reserve failed jobs" do
185
+ create_job :attempts => 50, :failed_at => described_class.db_time_now
186
+ described_class.reserve(worker).should be_nil
187
+ end
188
+
189
+ it "should not reserve jobs scheduled for the future" do
190
+ create_job :run_at => described_class.db_time_now + 1.minute
191
+ described_class.reserve(worker).should be_nil
192
+ end
193
+
194
+ it "should reserve jobs scheduled for the past" do
195
+ job = create_job :run_at => described_class.db_time_now - 1.minute
196
+ described_class.reserve(worker).should == job
197
+ end
198
+
199
+ it "should reserve jobs scheduled for the past when time zones are involved" do
200
+ Time.zone = 'US/Eastern'
201
+ job = create_job :run_at => described_class.db_time_now - 1.minute
202
+ described_class.reserve(worker).should == job
203
+ end
204
+
205
+ it "should not reserve jobs locked by other workers" do
206
+ job = create_job
207
+ other_worker = Delayed::Worker.new
208
+ other_worker.name = 'other_worker'
209
+ described_class.reserve(other_worker).should == job
210
+ described_class.reserve(worker).should be_nil
211
+ end
212
+
213
+ it "should reserve open jobs" do
214
+ job = create_job
215
+ described_class.reserve(worker).should == job
216
+ end
217
+
218
+ it "should reserve expired jobs" do
219
+ job = create_job(:locked_by => 'some other worker', :locked_at => described_class.db_time_now - Delayed::Worker.max_run_time - 1.minute)
220
+ described_class.reserve(worker).should == job
221
+ end
222
+
223
+ it "should reserve own jobs" do
224
+ job = create_job(:locked_by => worker.name, :locked_at => (described_class.db_time_now - 1.minutes))
225
+ described_class.reserve(worker).should == job
226
+ end
227
+ end
228
+
229
+ context "#name" do
230
+ it "should be the class name of the job that was enqueued" do
231
+ described_class.create(:payload_object => ErrorJob.new ).name.should == 'ErrorJob'
232
+ end
233
+
234
+ it "should be the method that will be called if its a performable method object" do
235
+ job = described_class.new(:payload_object => NamedJob.new)
236
+ job.name.should == 'named_job'
237
+ end
238
+
239
+ it "should be the instance method that will be called if its a performable method object" do
240
+ @job = Story.create(:text => "...").delay.save
241
+ @job.name.should == 'Story#save'
242
+ end
243
+
244
+ it "should parse from handler on deserialization error" do
245
+ job = Story.create(:text => "...").delay.text
246
+ job.payload_object.object.destroy
247
+ job.reload.name.should == 'Delayed::PerformableMethod'
248
+ end
249
+ end
250
+
251
+ context "worker prioritization" do
252
+ before(:each) do
253
+ Delayed::Worker.max_priority = nil
254
+ Delayed::Worker.min_priority = nil
255
+ end
256
+
257
+ it "should fetch jobs ordered by priority" do
258
+ 10.times { described_class.enqueue SimpleJob.new, :priority => rand(10) }
259
+ jobs = []
260
+ 10.times { jobs << described_class.reserve(worker) }
261
+ jobs.size.should == 10
262
+ jobs.each_cons(2) do |a, b|
263
+ a.priority.should <= b.priority
264
+ end
265
+ end
266
+
267
+ it "should only find jobs greater than or equal to min priority" do
268
+ min = 5
269
+ Delayed::Worker.min_priority = min
270
+ 10.times {|i| described_class.enqueue SimpleJob.new, :priority => i }
271
+ 5.times { described_class.reserve(worker).priority.should >= min }
272
+ end
273
+
274
+ it "should only find jobs less than or equal to max priority" do
275
+ max = 5
276
+ Delayed::Worker.max_priority = max
277
+ 10.times {|i| described_class.enqueue SimpleJob.new, :priority => i }
278
+ 5.times { described_class.reserve(worker).priority.should <= max }
279
+ end
280
+ end
281
+
282
+ context "clear_locks!" do
283
+ before do
284
+ @job = create_job(:locked_by => 'worker1', :locked_at => described_class.db_time_now)
285
+ end
286
+
287
+ it "should clear locks for the given worker" do
288
+ described_class.clear_locks!('worker1')
289
+ described_class.reserve(worker).should == @job
290
+ end
291
+
292
+ it "should not clear locks for other workers" do
293
+ described_class.clear_locks!('different_worker')
294
+ described_class.reserve(worker).should_not == @job
295
+ end
296
+ end
297
+
298
+ context "unlock" do
299
+ before do
300
+ @job = create_job(:locked_by => 'worker', :locked_at => described_class.db_time_now)
301
+ end
302
+
303
+ it "should clear locks" do
304
+ @job.unlock
305
+ @job.locked_by.should be_nil
306
+ @job.locked_at.should be_nil
307
+ end
308
+ end
309
+
310
+ context "large handler" do
311
+ before do
312
+ text = "Lorem ipsum dolor sit amet. " * 1000
313
+ @job = described_class.enqueue Delayed::PerformableMethod.new(text, :length, {})
314
+ end
315
+
316
+ it "should have an id" do
317
+ @job.id.should_not be_nil
318
+ end
319
+ end
320
+
321
+ context "named queues" do
322
+ context "when worker has one queue set" do
323
+ before(:each) do
324
+ worker.queues = ['large']
325
+ end
326
+
327
+ it "should only work off jobs which are from its queue" do
328
+ SimpleJob.runs.should == 0
329
+
330
+ create_job(:queue => "large")
331
+ create_job(:queue => "small")
332
+ worker.work_off
333
+
334
+ SimpleJob.runs.should == 1
335
+ end
336
+ end
337
+
338
+ context "when worker has two queue set" do
339
+ before(:each) do
340
+ worker.queues = ['large', 'small']
341
+ end
342
+
343
+ it "should only work off jobs which are from its queue" do
344
+ SimpleJob.runs.should == 0
345
+
346
+ create_job(:queue => "large")
347
+ create_job(:queue => "small")
348
+ create_job(:queue => "medium")
349
+ create_job
350
+ worker.work_off
351
+
352
+ SimpleJob.runs.should == 2
353
+ end
354
+ end
355
+
356
+ context "when worker does not have queue set" do
357
+ before(:each) do
358
+ worker.queues = []
359
+ end
360
+
361
+ it "should work off all jobs" do
362
+ SimpleJob.runs.should == 0
363
+
364
+ create_job(:queue => "one")
365
+ create_job(:queue => "two")
366
+ create_job
367
+ worker.work_off
368
+
369
+ SimpleJob.runs.should == 3
370
+ end
371
+ end
372
+ end
373
+
374
+ context "max_attempts" do
375
+ before(:each) do
376
+ @job = described_class.enqueue SimpleJob.new
377
+ end
378
+
379
+ it 'should not be defined' do
380
+ @job.max_attempts.should be_nil
381
+ end
382
+
383
+ it 'should use the max_retries value on the payload when defined' do
384
+ @job.payload_object.stub!(:max_attempts).and_return(99)
385
+ @job.max_attempts.should == 99
386
+ end
387
+ end
388
+
389
+ describe "yaml serialization" do
390
+ it "should reload changed attributes" do
391
+ story = Story.create(:text => 'hello')
392
+ job = story.delay.tell
393
+ story.update_attributes :text => 'goodbye'
394
+ job.reload.payload_object.object.text.should == 'goodbye'
395
+ end
396
+
397
+ it "should raise deserialization error for destroyed records" do
398
+ story = Story.create(:text => 'hello')
399
+ job = story.delay.tell
400
+ story.destroy
401
+ lambda {
402
+ job.reload.payload_object
403
+ }.should raise_error(Delayed::DeserializationError)
404
+ end
405
+ end
406
+
407
+ describe "worker integration" do
408
+ before do
409
+ Delayed::Job.delete_all
410
+ SimpleJob.runs = 0
411
+ end
412
+
413
+ describe "running a job" do
414
+ it "should fail after Worker.max_run_time" do
415
+ begin
416
+ old_max_run_time = Delayed::Worker.max_run_time
417
+ Delayed::Worker.max_run_time = 1.second
418
+ @job = Delayed::Job.create :payload_object => LongRunningJob.new
419
+ worker.run(@job)
420
+ @job.reload.last_error.should =~ /expired/
421
+ @job.attempts.should == 1
422
+ ensure
423
+ Delayed::Worker.max_run_time = old_max_run_time
424
+ end
425
+ end
426
+
427
+ context "when the job raises a deserialization error" do
428
+ it "should mark the job as failed" do
429
+ Delayed::Worker.destroy_failed_jobs = false
430
+ job = described_class.create! :handler => "--- !ruby/object:JobThatDoesNotExist {}"
431
+ worker.work_off
432
+ job.reload
433
+ job.failed_at.should_not be_nil
434
+ end
435
+ end
436
+ end
437
+
438
+ describe "failed jobs" do
439
+ before do
440
+ # reset defaults
441
+ Delayed::Worker.destroy_failed_jobs = true
442
+ Delayed::Worker.max_attempts = 25
443
+
444
+ @job = Delayed::Job.enqueue(ErrorJob.new)
445
+ end
446
+
447
+ it "should record last_error when destroy_failed_jobs = false, max_attempts = 1" do
448
+ Delayed::Worker.destroy_failed_jobs = false
449
+ Delayed::Worker.max_attempts = 1
450
+ worker.run(@job)
451
+ @job.reload
452
+ @job.last_error.should =~ /did not work/
453
+ @job.attempts.should == 1
454
+ @job.failed_at.should_not be_nil
455
+ end
456
+
457
+ it "should re-schedule jobs after failing" do
458
+ worker.work_off
459
+ @job.reload
460
+ @job.last_error.should =~ /did not work/
461
+ @job.last_error.should =~ /sample_jobs.rb:\d+:in `perform'/
462
+ @job.attempts.should == 1
463
+ @job.run_at.should > Delayed::Job.db_time_now - 10.minutes
464
+ @job.run_at.should < Delayed::Job.db_time_now + 10.minutes
465
+ @job.locked_by.should be_nil
466
+ @job.locked_at.should be_nil
467
+ end
468
+
469
+ it 'should re-schedule with handler provided time if present' do
470
+ @job = Delayed::Job.enqueue(CustomRescheduleJob.new(99.minutes))
471
+ worker.run(@job)
472
+ @job.reload
473
+
474
+ (Delayed::Job.db_time_now + 99.minutes - @job.run_at).abs.should < 1
475
+ end
476
+
477
+ it "should not fail when the triggered error doesn't have a message" do
478
+ error_with_nil_message = StandardError.new
479
+ error_with_nil_message.stub!(:message).and_return nil
480
+ @job.stub!(:invoke_job).and_raise error_with_nil_message
481
+ lambda{worker.run(@job)}.should_not raise_error
482
+ end
483
+ end
484
+
485
+ context "reschedule" do
486
+ before do
487
+ @job = Delayed::Job.create :payload_object => SimpleJob.new
488
+ end
489
+
490
+ share_examples_for "any failure more than Worker.max_attempts times" do
491
+ context "when the job's payload has a #failure hook" do
492
+ before do
493
+ @job = Delayed::Job.create :payload_object => OnPermanentFailureJob.new
494
+ @job.payload_object.should respond_to :failure
495
+ end
496
+
497
+ it "should run that hook" do
498
+ @job.payload_object.should_receive :failure
499
+ worker.reschedule(@job)
500
+ end
501
+ end
502
+
503
+ context "when the job's payload has no #failure hook" do
504
+ # It's a little tricky to test this in a straightforward way,
505
+ # because putting a should_not_receive expectation on
506
+ # @job.payload_object.failure makes that object
507
+ # incorrectly return true to
508
+ # payload_object.respond_to? :failure, which is what
509
+ # reschedule uses to decide whether to call failure.
510
+ # So instead, we just make sure that the payload_object as it
511
+ # already stands doesn't respond_to? failure, then
512
+ # shove it through the iterated reschedule loop and make sure we
513
+ # don't get a NoMethodError (caused by calling that nonexistent
514
+ # failure method).
515
+
516
+ before do
517
+ @job.payload_object.should_not respond_to(:failure)
518
+ end
519
+
520
+ it "should not try to run that hook" do
521
+ lambda do
522
+ Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
523
+ end.should_not raise_exception(NoMethodError)
524
+ end
525
+ end
526
+ end
527
+
528
+ context "and we want to destroy jobs" do
529
+ before do
530
+ Delayed::Worker.destroy_failed_jobs = true
531
+ end
532
+
533
+ it_should_behave_like "any failure more than Worker.max_attempts times"
534
+
535
+ it "should be destroyed if it failed more than Worker.max_attempts times" do
536
+ @job.should_receive(:destroy)
537
+ Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
538
+ end
539
+
540
+ it "should not be destroyed if failed fewer than Worker.max_attempts times" do
541
+ @job.should_not_receive(:destroy)
542
+ (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) }
543
+ end
544
+ end
545
+
546
+ context "and we don't want to destroy jobs" do
547
+ before do
548
+ Delayed::Worker.destroy_failed_jobs = false
549
+ end
550
+
551
+ it_should_behave_like "any failure more than Worker.max_attempts times"
552
+
553
+ it "should be failed if it failed more than Worker.max_attempts times" do
554
+ @job.reload.failed_at.should == nil
555
+ Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
556
+ @job.reload.failed_at.should_not == nil
557
+ end
558
+
559
+ it "should not be failed if it failed fewer than Worker.max_attempts times" do
560
+ (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) }
561
+ @job.reload.failed_at.should == nil
562
+ end
563
+ end
564
+ end
565
+ end
566
+ end
@@ -0,0 +1,101 @@
1
+ require 'daemons'
2
+ require 'optparse'
3
+
4
+ module Delayed
5
+ class Command
6
+ attr_accessor :worker_count
7
+
8
+ def initialize(args)
9
+ @options = {
10
+ :quiet => true,
11
+ :pid_dir => "#{Rails.root}/tmp/pids"
12
+ }
13
+
14
+ @worker_count = 1
15
+ @monitor = false
16
+
17
+ opts = OptionParser.new do |opts|
18
+ opts.banner = "Usage: #{File.basename($0)} [options] start|stop|restart|run"
19
+
20
+ opts.on('-h', '--help', 'Show this message') do
21
+ puts opts
22
+ exit 1
23
+ end
24
+ opts.on('-e', '--environment=NAME', 'Specifies the environment to run this delayed jobs under (test/development/production).') do |e|
25
+ STDERR.puts "The -e/--environment option has been deprecated and has no effect. Use RAILS_ENV and see http://github.com/collectiveidea/delayed_job/issues/#issue/7"
26
+ end
27
+ opts.on('--min-priority N', 'Minimum priority of jobs to run.') do |n|
28
+ @options[:min_priority] = n
29
+ end
30
+ opts.on('--max-priority N', 'Maximum priority of jobs to run.') do |n|
31
+ @options[:max_priority] = n
32
+ end
33
+ opts.on('-n', '--number_of_workers=workers', "Number of unique workers to spawn") do |worker_count|
34
+ @worker_count = worker_count.to_i rescue 1
35
+ end
36
+ opts.on('--pid-dir=DIR', 'Specifies an alternate directory in which to store the process ids.') do |dir|
37
+ @options[:pid_dir] = dir
38
+ end
39
+ opts.on('-i', '--identifier=n', 'A numeric identifier for the worker.') do |n|
40
+ @options[:identifier] = n
41
+ end
42
+ opts.on('-m', '--monitor', 'Start monitor process.') do
43
+ @monitor = true
44
+ end
45
+ opts.on('--sleep-delay N', "Amount of time to sleep when no jobs are found") do |n|
46
+ @options[:sleep_delay] = n
47
+ end
48
+ opts.on('-p', '--prefix NAME', "String to be prefixed to worker process names") do |prefix|
49
+ @options[:prefix] = prefix
50
+ end
51
+ opts.on('--queues=queues', "Specify which queue DJ must look up for jobs") do |queues|
52
+ @options[:queues] = queues.split(',')
53
+ end
54
+ opts.on('--queue=queue', "Specify which queue DJ must look up for jobs") do |queue|
55
+ @options[:queues] = queue.split(',')
56
+ end
57
+ end
58
+ @args = opts.parse!(args)
59
+ end
60
+
61
+ def daemonize
62
+ dir = @options[:pid_dir]
63
+ Dir.mkdir(dir) unless File.exists?(dir)
64
+
65
+ if @worker_count > 1 && @options[:identifier]
66
+ raise ArgumentError, 'Cannot specify both --number-of-workers and --identifier'
67
+ elsif @worker_count == 1 && @options[:identifier]
68
+ process_name = "delayed_job.#{@options[:identifier]}"
69
+ run_process(process_name, dir)
70
+ else
71
+ worker_count.times do |worker_index|
72
+ process_name = worker_count == 1 ? "delayed_job" : "delayed_job.#{worker_index}"
73
+ run_process(process_name, dir)
74
+ end
75
+ end
76
+ end
77
+
78
+ def run_process(process_name, dir)
79
+ Delayed::Worker.before_fork
80
+ Daemons.run_proc(process_name, :dir => dir, :dir_mode => :normal, :monitor => @monitor, :ARGV => @args) do |*args|
81
+ $0 = File.join(@options[:prefix], process_name) if @options[:prefix]
82
+ run process_name
83
+ end
84
+ end
85
+
86
+ def run(worker_name = nil)
87
+ Dir.chdir(Rails.root)
88
+
89
+ Delayed::Worker.after_fork
90
+ Delayed::Worker.logger = Logger.new(File.join(Rails.root, 'log', 'delayed_job.log'))
91
+
92
+ worker = Delayed::Worker.new(@options)
93
+ worker.name_prefix = "#{worker_name} "
94
+ worker.start
95
+ rescue => e
96
+ Rails.logger.fatal e
97
+ STDERR.puts e.message
98
+ exit 1
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,4 @@
1
+ module Delayed
2
+ class DeserializationError < StandardError
3
+ end
4
+ end