delayed_job_unique_key 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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