delayed_job_unique_key 0.0.4 → 0.1.0
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.
- data/lib/delayed_job_unique_key.rb +8 -0
- data/lib/delayed_job_unique_key/active_record_job.rb +12 -0
- data/lib/delayed_job_unique_key/base.rb +52 -0
- data/lib/delayed_job_unique_key/version.rb +3 -0
- data/lib/generators/delayed_job_unique_key/install_generator.rb +17 -0
- data/lib/generators/delayed_job_unique_key/templates/add_unique_key_migration.rb +11 -0
- metadata +63 -174
- data/MIT-LICENSE +0 -20
- data/README.textile +0 -246
- data/contrib/delayed_job.monitrc +0 -14
- data/contrib/delayed_job_multiple.monitrc +0 -23
- data/lib/delayed/backend/base.rb +0 -152
- data/lib/delayed/backend/shared_spec.rb +0 -566
- data/lib/delayed/command.rb +0 -101
- data/lib/delayed/deserialization_error.rb +0 -4
- data/lib/delayed/lifecycle.rb +0 -84
- data/lib/delayed/message_sending.rb +0 -54
- data/lib/delayed/performable_mailer.rb +0 -21
- data/lib/delayed/performable_method.rb +0 -33
- data/lib/delayed/plugin.rb +0 -15
- data/lib/delayed/plugins/clear_locks.rb +0 -15
- data/lib/delayed/psych_ext.rb +0 -75
- data/lib/delayed/railtie.rb +0 -16
- data/lib/delayed/recipes.rb +0 -50
- data/lib/delayed/serialization/active_record.rb +0 -19
- data/lib/delayed/syck_ext.rb +0 -34
- data/lib/delayed/tasks.rb +0 -11
- data/lib/delayed/worker.rb +0 -222
- data/lib/delayed/yaml_ext.rb +0 -10
- data/lib/delayed_job.rb +0 -22
- data/lib/generators/delayed_job/delayed_job_generator.rb +0 -11
- data/lib/generators/delayed_job/templates/script +0 -5
- data/recipes/delayed_job.rb +0 -1
- data/spec/autoloaded/clazz.rb +0 -7
- data/spec/autoloaded/instance_clazz.rb +0 -6
- data/spec/autoloaded/instance_struct.rb +0 -6
- data/spec/autoloaded/struct.rb +0 -7
- data/spec/delayed/backend/test.rb +0 -113
- data/spec/delayed/serialization/test.rb +0 -0
- data/spec/fixtures/bad_alias.yml +0 -1
- data/spec/lifecycle_spec.rb +0 -107
- data/spec/message_sending_spec.rb +0 -116
- data/spec/performable_mailer_spec.rb +0 -46
- data/spec/performable_method_spec.rb +0 -89
- data/spec/sample_jobs.rb +0 -75
- data/spec/spec_helper.rb +0 -45
- data/spec/test_backend_spec.rb +0 -13
- data/spec/worker_spec.rb +0 -19
- data/spec/yaml_ext_spec.rb +0 -41
data/contrib/delayed_job.monitrc
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
# an example Monit configuration file for delayed_job
|
2
|
-
# See: http://stackoverflow.com/questions/1226302/how-to-monitor-delayedjob-with-monit/1285611
|
3
|
-
#
|
4
|
-
# To use:
|
5
|
-
# 1. copy to /var/www/apps/{app_name}/shared/delayed_job.monitrc
|
6
|
-
# 2. replace {app_name} as appropriate
|
7
|
-
# 3. add this to your /etc/monit/monitrc
|
8
|
-
#
|
9
|
-
# include /var/www/apps/{app_name}/shared/delayed_job.monitrc
|
10
|
-
|
11
|
-
check process delayed_job
|
12
|
-
with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.pid
|
13
|
-
start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start"
|
14
|
-
stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop"
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# an example Monit configuration file for delayed_job running multiple processes
|
2
|
-
#
|
3
|
-
# To use:
|
4
|
-
# 1. copy to /var/www/apps/{app_name}/shared/delayed_job.monitrc
|
5
|
-
# 2. replace {app_name} as appropriate
|
6
|
-
# 3. add this to your /etc/monit/monitrc
|
7
|
-
#
|
8
|
-
# include /var/www/apps/{app_name}/shared/delayed_job.monitrc
|
9
|
-
|
10
|
-
check process delayed_job_0
|
11
|
-
with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.0.pid
|
12
|
-
start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start -i 0"
|
13
|
-
stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop -i 0"
|
14
|
-
|
15
|
-
check process delayed_job_1
|
16
|
-
with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.1.pid
|
17
|
-
start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start -i 1"
|
18
|
-
stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop -i 1"
|
19
|
-
|
20
|
-
check process delayed_job_2
|
21
|
-
with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.2.pid
|
22
|
-
start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start -i 2"
|
23
|
-
stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop -i 2"
|
data/lib/delayed/backend/base.rb
DELETED
@@ -1,152 +0,0 @@
|
|
1
|
-
module Delayed
|
2
|
-
module Backend
|
3
|
-
module Base
|
4
|
-
def self.included(base)
|
5
|
-
base.extend ClassMethods
|
6
|
-
end
|
7
|
-
|
8
|
-
module ClassMethods
|
9
|
-
# Add a job to the queue
|
10
|
-
def enqueue(*args)
|
11
|
-
options = {
|
12
|
-
:priority => Delayed::Worker.default_priority
|
13
|
-
}.merge!(args.extract_options!)
|
14
|
-
|
15
|
-
options[:payload_object] ||= args.shift
|
16
|
-
|
17
|
-
if args.size > 0
|
18
|
-
warn "[DEPRECATION] Passing multiple arguments to `#enqueue` is deprecated. Pass a hash with :priority and :run_at."
|
19
|
-
options[:priority] = args.first || options[:priority]
|
20
|
-
options[:unique_key] = options[:unique_key]
|
21
|
-
options[:run_at] = args[1]
|
22
|
-
end
|
23
|
-
|
24
|
-
unless options[:payload_object].respond_to?(:perform)
|
25
|
-
raise ArgumentError, 'Cannot enqueue items which do not respond to perform'
|
26
|
-
end
|
27
|
-
|
28
|
-
if Delayed::Worker.delay_jobs
|
29
|
-
self.new(options).tap do |job|
|
30
|
-
Delayed::Worker.lifecycle.run_callbacks(:enqueue, job) do
|
31
|
-
job.hook(:enqueue)
|
32
|
-
job.save
|
33
|
-
end
|
34
|
-
end
|
35
|
-
else
|
36
|
-
Delayed::Job.new(:payload_object => options[:payload_object]).tap do |job|
|
37
|
-
job.invoke_job
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def reserve(worker, max_run_time = Worker.max_run_time)
|
43
|
-
# We get up to 5 jobs from the db. In case we cannot get exclusive access to a job we try the next.
|
44
|
-
# this leads to a more even distribution of jobs across the worker processes
|
45
|
-
find_available(worker.name, 5, max_run_time).detect do |job|
|
46
|
-
job.lock_exclusively!(max_run_time, worker.name)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
# Hook method that is called before a new worker is forked
|
51
|
-
def before_fork
|
52
|
-
end
|
53
|
-
|
54
|
-
# Hook method that is called after a new worker is forked
|
55
|
-
def after_fork
|
56
|
-
end
|
57
|
-
|
58
|
-
def work_off(num = 100)
|
59
|
-
warn "[DEPRECATION] `Delayed::Job.work_off` is deprecated. Use `Delayed::Worker.new.work_off instead."
|
60
|
-
Delayed::Worker.new.work_off(num)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def failed?
|
65
|
-
failed_at
|
66
|
-
end
|
67
|
-
alias_method :failed, :failed?
|
68
|
-
|
69
|
-
ParseObjectFromYaml = /\!ruby\/\w+\:([^\s]+)/
|
70
|
-
|
71
|
-
def name
|
72
|
-
@name ||= payload_object.respond_to?(:display_name) ?
|
73
|
-
payload_object.display_name :
|
74
|
-
payload_object.class.name
|
75
|
-
rescue DeserializationError
|
76
|
-
ParseObjectFromYaml.match(handler)[1]
|
77
|
-
end
|
78
|
-
|
79
|
-
def payload_object=(object)
|
80
|
-
@payload_object = object
|
81
|
-
self.handler = object.to_yaml
|
82
|
-
end
|
83
|
-
|
84
|
-
def payload_object
|
85
|
-
@payload_object ||= YAML.load(self.handler)
|
86
|
-
rescue TypeError, LoadError, NameError, ArgumentError => e
|
87
|
-
raise DeserializationError,
|
88
|
-
"Job failed to load: #{e.message}. Handler: #{handler.inspect}"
|
89
|
-
end
|
90
|
-
|
91
|
-
def invoke_job
|
92
|
-
Delayed::Worker.lifecycle.run_callbacks(:invoke_job, self) do
|
93
|
-
begin
|
94
|
-
hook :before
|
95
|
-
payload_object.perform
|
96
|
-
hook :success
|
97
|
-
rescue Exception => e
|
98
|
-
hook :error, e
|
99
|
-
raise e
|
100
|
-
ensure
|
101
|
-
hook :after
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
# Unlock this job (note: not saved to DB)
|
107
|
-
def unlock
|
108
|
-
self.locked_at = nil
|
109
|
-
self.locked_by = nil
|
110
|
-
end
|
111
|
-
|
112
|
-
def hook(name, *args)
|
113
|
-
if payload_object.respond_to?(name)
|
114
|
-
method = payload_object.method(name)
|
115
|
-
method.arity == 0 ? method.call : method.call(self, *args)
|
116
|
-
end
|
117
|
-
rescue DeserializationError
|
118
|
-
# do nothing
|
119
|
-
end
|
120
|
-
|
121
|
-
def reschedule_at
|
122
|
-
payload_object.respond_to?(:reschedule_at) ?
|
123
|
-
payload_object.reschedule_at(self.class.db_time_now, attempts) :
|
124
|
-
self.class.db_time_now + (attempts ** 4) + 5
|
125
|
-
end
|
126
|
-
|
127
|
-
def max_attempts
|
128
|
-
payload_object.max_attempts if payload_object.respond_to?(:max_attempts)
|
129
|
-
end
|
130
|
-
|
131
|
-
def fail!
|
132
|
-
update_attributes(:failed_at => self.class.db_time_now)
|
133
|
-
end
|
134
|
-
|
135
|
-
protected
|
136
|
-
|
137
|
-
def set_default_run_at
|
138
|
-
self.run_at ||= self.class.db_time_now
|
139
|
-
end
|
140
|
-
|
141
|
-
def check_unique_key
|
142
|
-
return true if !self.respond_to?(:unique_key) || self.unique_key.blank?
|
143
|
-
self.class.where(:unique_key => self.unique_key, :locked_at => nil).first.nil?
|
144
|
-
end
|
145
|
-
|
146
|
-
# Call during reload operation to clear out internal state
|
147
|
-
def reset
|
148
|
-
@payload_object = nil
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
@@ -1,566 +0,0 @@
|
|
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
|