jmcnevin-delayed_job 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.gitignore +3 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.textile +213 -0
  4. data/Rakefile +45 -0
  5. data/VERSION +1 -0
  6. data/benchmarks.rb +33 -0
  7. data/contrib/delayed_job.monitrc +14 -0
  8. data/contrib/delayed_job_multiple.monitrc +23 -0
  9. data/delayed_job.gemspec +107 -0
  10. data/generators/delayed_job/delayed_job_generator.rb +21 -0
  11. data/generators/delayed_job/templates/migration.rb +21 -0
  12. data/init.rb +1 -0
  13. data/lib/delayed/backend/active_record.rb +90 -0
  14. data/lib/delayed/backend/base.rb +111 -0
  15. data/lib/delayed/backend/data_mapper.rb +125 -0
  16. data/lib/delayed/backend/mongo_mapper.rb +110 -0
  17. data/lib/delayed/message_sending.rb +22 -0
  18. data/lib/delayed/performable_method.rb +62 -0
  19. data/lib/delayed/railtie.rb +10 -0
  20. data/lib/delayed/recipes.rb +31 -0
  21. data/lib/delayed/tasks.rb +15 -0
  22. data/lib/delayed/worker.rb +199 -0
  23. data/lib/delayed_job.rb +14 -0
  24. data/rails/init.rb +5 -0
  25. data/recipes/delayed_job.rb +1 -0
  26. data/spec/backend/active_record_job_spec.rb +46 -0
  27. data/spec/backend/data_mapper_job_spec.rb +16 -0
  28. data/spec/backend/mongo_mapper_job_spec.rb +94 -0
  29. data/spec/backend/shared_backend_spec.rb +265 -0
  30. data/spec/delayed_method_spec.rb +59 -0
  31. data/spec/performable_method_spec.rb +42 -0
  32. data/spec/sample_jobs.rb +25 -0
  33. data/spec/setup/active_record.rb +33 -0
  34. data/spec/setup/data_mapper.rb +8 -0
  35. data/spec/setup/mongo_mapper.rb +17 -0
  36. data/spec/spec_helper.rb +26 -0
  37. data/spec/story_spec.rb +17 -0
  38. data/spec/worker_spec.rb +216 -0
  39. data/tasks/jobs.rake +1 -0
  40. metadata +241 -0
@@ -0,0 +1,26 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'rubygems'
4
+ require 'spec'
5
+ require 'logger'
6
+
7
+ require 'delayed_job'
8
+ require 'sample_jobs'
9
+
10
+ Delayed::Worker.logger = Logger.new('/tmp/dj.log')
11
+ RAILS_ENV = 'test'
12
+
13
+ # determine the available backends
14
+ BACKENDS = []
15
+ Dir.glob("#{File.dirname(__FILE__)}/setup/*.rb") do |backend|
16
+ begin
17
+ backend = File.basename(backend, '.rb')
18
+ require "setup/#{backend}"
19
+ require "backend/#{backend}_job_spec"
20
+ BACKENDS << backend.to_sym
21
+ rescue LoadError
22
+ puts "Unable to load #{backend} backend! #{$!}"
23
+ end
24
+ end
25
+
26
+ Delayed::Worker.backend = BACKENDS.first
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe "A story" do
4
+
5
+ before(:all) do
6
+ @story = Story.create :text => "Once upon a time..."
7
+ end
8
+
9
+ it "should be shared" do
10
+ @story.tell.should == 'Once upon a time...'
11
+ end
12
+
13
+ it "should not return its result if it storytelling is delayed" do
14
+ @story.send_later(:tell).should_not == 'Once upon a time...'
15
+ end
16
+
17
+ end
@@ -0,0 +1,216 @@
1
+ require 'spec_helper'
2
+
3
+ describe Delayed::Worker do
4
+ def job_create(opts = {})
5
+ Delayed::Job.create(opts.merge(:payload_object => SimpleJob.new))
6
+ end
7
+
8
+ describe "backend=" do
9
+ it "should set the Delayed::Job constant to the backend" do
10
+ @clazz = Class.new
11
+ Delayed::Worker.backend = @clazz
12
+ Delayed::Job.should == @clazz
13
+ end
14
+
15
+ it "should set backend with a symbol" do
16
+ Delayed::Worker.backend = Class.new
17
+ Delayed::Worker.backend = :active_record
18
+ Delayed::Worker.backend.should == Delayed::Backend::ActiveRecord::Job
19
+ end
20
+ end
21
+
22
+ BACKENDS.each do |backend|
23
+ describe "with the #{backend} backend" do
24
+ before do
25
+ Delayed::Worker.backend = backend
26
+ Delayed::Job.delete_all
27
+
28
+ @worker = Delayed::Worker.new(:max_priority => nil, :min_priority => nil, :quiet => true)
29
+
30
+ SimpleJob.runs = 0
31
+ end
32
+
33
+ describe "running a job" do
34
+ it "should fail after Worker.max_run_time" do
35
+ begin
36
+ old_max_run_time = Delayed::Worker.max_run_time
37
+ Delayed::Worker.max_run_time = 1.second
38
+ @job = Delayed::Job.create :payload_object => LongRunningJob.new
39
+ @worker.run(@job)
40
+ @job.reload.last_error.should =~ /expired/
41
+ @job.attempts.should == 1
42
+ ensure
43
+ Delayed::Worker.max_run_time = old_max_run_time
44
+ end
45
+ end
46
+ end
47
+
48
+ context "worker prioritization" do
49
+ before(:each) do
50
+ @worker = Delayed::Worker.new(:max_priority => 5, :min_priority => -5, :quiet => true)
51
+ end
52
+
53
+ it "should only work_off jobs that are >= min_priority" do
54
+ SimpleJob.runs.should == 0
55
+
56
+ job_create(:priority => -10)
57
+ job_create(:priority => 0)
58
+ @worker.work_off
59
+
60
+ SimpleJob.runs.should == 1
61
+ end
62
+
63
+ it "should only work_off jobs that are <= max_priority" do
64
+ SimpleJob.runs.should == 0
65
+
66
+ job_create(:priority => 10)
67
+ job_create(:priority => 0)
68
+
69
+ @worker.work_off
70
+
71
+ SimpleJob.runs.should == 1
72
+ end
73
+ end
74
+
75
+ context "while running with locked and expired jobs" do
76
+ before(:each) do
77
+ @worker.name = 'worker1'
78
+ end
79
+
80
+ it "should not run jobs locked by another worker" do
81
+ job_create(:locked_by => 'other_worker', :locked_at => (Delayed::Job.db_time_now - 1.minutes))
82
+ lambda { @worker.work_off }.should_not change { SimpleJob.runs }
83
+ end
84
+
85
+ it "should run open jobs" do
86
+ job_create
87
+ lambda { @worker.work_off }.should change { SimpleJob.runs }.from(0).to(1)
88
+ end
89
+
90
+ it "should run expired jobs" do
91
+ expired_time = Delayed::Job.db_time_now - (1.minutes + Delayed::Worker.max_run_time)
92
+ job_create(:locked_by => 'other_worker', :locked_at => expired_time)
93
+ lambda { @worker.work_off }.should change { SimpleJob.runs }.from(0).to(1)
94
+ end
95
+
96
+ it "should run own jobs" do
97
+ job_create(:locked_by => @worker.name, :locked_at => (Delayed::Job.db_time_now - 1.minutes))
98
+ lambda { @worker.work_off }.should change { SimpleJob.runs }.from(0).to(1)
99
+ end
100
+ end
101
+
102
+ describe "failed jobs" do
103
+ before do
104
+ # reset defaults
105
+ Delayed::Worker.destroy_failed_jobs = true
106
+ Delayed::Worker.max_attempts = 25
107
+
108
+ @job = Delayed::Job.enqueue ErrorJob.new
109
+ end
110
+
111
+ it "should record last_error when destroy_failed_jobs = false, max_attempts = 1" do
112
+ Delayed::Worker.destroy_failed_jobs = false
113
+ Delayed::Worker.max_attempts = 1
114
+ @worker.run(@job)
115
+ @job.reload
116
+ @job.last_error.should =~ /did not work/
117
+ @job.last_error.should =~ /worker_spec.rb/
118
+ @job.attempts.should == 1
119
+ @job.failed_at.should_not be_nil
120
+ end
121
+
122
+ it "should re-schedule jobs after failing" do
123
+ @worker.run(@job)
124
+ @job.reload
125
+ @job.last_error.should =~ /did not work/
126
+ @job.last_error.should =~ /sample_jobs.rb:8:in `perform'/
127
+ @job.attempts.should == 1
128
+ @job.run_at.should > Delayed::Job.db_time_now - 10.minutes
129
+ @job.run_at.should < Delayed::Job.db_time_now + 10.minutes
130
+ end
131
+ end
132
+
133
+ context "reschedule" do
134
+ before do
135
+ @job = Delayed::Job.create :payload_object => SimpleJob.new
136
+ end
137
+
138
+ share_examples_for "any failure more than Worker.max_attempts times" do
139
+ context "when the job's payload has an #on_permanent_failure hook" do
140
+ before do
141
+ @job = Delayed::Job.create :payload_object => OnPermanentFailureJob.new
142
+ @job.payload_object.should respond_to :on_permanent_failure
143
+ end
144
+
145
+ it "should run that hook" do
146
+ @job.payload_object.should_receive :on_permanent_failure
147
+ Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }
148
+ end
149
+ end
150
+
151
+ context "when the job's payload has no #on_permanent_failure hook" do
152
+ # It's a little tricky to test this in a straightforward way,
153
+ # because putting a should_not_receive expectation on
154
+ # @job.payload_object.on_permanent_failure makes that object
155
+ # incorrectly return true to
156
+ # payload_object.respond_to? :on_permanent_failure, which is what
157
+ # reschedule uses to decide whether to call on_permanent_failure.
158
+ # So instead, we just make sure that the payload_object as it
159
+ # already stands doesn't respond_to? on_permanent_failure, then
160
+ # shove it through the iterated reschedule loop and make sure we
161
+ # don't get a NoMethodError (caused by calling that nonexistent
162
+ # on_permanent_failure method).
163
+
164
+ before do
165
+ @job.payload_object.should_not respond_to(:on_permanent_failure)
166
+ end
167
+
168
+ it "should not try to run that hook" do
169
+ lambda do
170
+ Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }
171
+ end.should_not raise_exception(NoMethodError)
172
+ end
173
+ end
174
+ end
175
+
176
+ context "and we want to destroy jobs" do
177
+ before do
178
+ Delayed::Worker.destroy_failed_jobs = true
179
+ end
180
+
181
+ it_should_behave_like "any failure more than Worker.max_attempts times"
182
+
183
+ it "should be destroyed if it failed more than Worker.max_attempts times" do
184
+ @job.should_receive(:destroy)
185
+ Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }
186
+ end
187
+
188
+ it "should not be destroyed if failed fewer than Worker.max_attempts times" do
189
+ @job.should_not_receive(:destroy)
190
+ (Delayed::Worker.max_attempts - 1).times { @worker.reschedule(@job) }
191
+ end
192
+ end
193
+
194
+ context "and we don't want to destroy jobs" do
195
+ before do
196
+ Delayed::Worker.destroy_failed_jobs = false
197
+ end
198
+
199
+ it_should_behave_like "any failure more than Worker.max_attempts times"
200
+
201
+ it "should be failed if it failed more than Worker.max_attempts times" do
202
+ @job.reload.failed_at.should == nil
203
+ Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }
204
+ @job.reload.failed_at.should_not == nil
205
+ end
206
+
207
+ it "should not be failed if it failed fewer than Worker.max_attempts times" do
208
+ (Delayed::Worker.max_attempts - 1).times { @worker.reschedule(@job) }
209
+ @job.reload.failed_at.should == nil
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
215
+
216
+ end
data/tasks/jobs.rake ADDED
@@ -0,0 +1 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'delayed', 'tasks'))
metadata ADDED
@@ -0,0 +1,241 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jmcnevin-delayed_job
3
+ version: !ruby/object:Gem::Version
4
+ hash: 7
5
+ prerelease: false
6
+ segments:
7
+ - 2
8
+ - 0
9
+ - 4
10
+ version: 2.0.4
11
+ platform: ruby
12
+ authors:
13
+ - Brandon Keepers
14
+ - "Tobias L\xC3\xBCtke"
15
+ - Jeremy McNevin
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2010-08-26 00:00:00 -07:00
21
+ default_executable:
22
+ dependencies:
23
+ - !ruby/object:Gem::Dependency
24
+ name: rspec
25
+ prerelease: false
26
+ requirement: &id001 !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ hash: 3
32
+ segments:
33
+ - 0
34
+ version: "0"
35
+ type: :development
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: sqlite3-ruby
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ type: :development
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: mongo_mapper
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ type: :development
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: dm-core
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ type: :development
78
+ version_requirements: *id004
79
+ - !ruby/object:Gem::Dependency
80
+ name: dm-observer
81
+ prerelease: false
82
+ requirement: &id005 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
90
+ version: "0"
91
+ type: :development
92
+ version_requirements: *id005
93
+ - !ruby/object:Gem::Dependency
94
+ name: dm-aggregates
95
+ prerelease: false
96
+ requirement: &id006 !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ hash: 3
102
+ segments:
103
+ - 0
104
+ version: "0"
105
+ type: :development
106
+ version_requirements: *id006
107
+ - !ruby/object:Gem::Dependency
108
+ name: dm-validations
109
+ prerelease: false
110
+ requirement: &id007 !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ hash: 3
116
+ segments:
117
+ - 0
118
+ version: "0"
119
+ type: :development
120
+ version_requirements: *id007
121
+ - !ruby/object:Gem::Dependency
122
+ name: do_sqlite3
123
+ prerelease: false
124
+ requirement: &id008 !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ hash: 3
130
+ segments:
131
+ - 0
132
+ version: "0"
133
+ type: :development
134
+ version_requirements: *id008
135
+ - !ruby/object:Gem::Dependency
136
+ name: database_cleaner
137
+ prerelease: false
138
+ requirement: &id009 !ruby/object:Gem::Requirement
139
+ none: false
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ hash: 3
144
+ segments:
145
+ - 0
146
+ version: "0"
147
+ type: :development
148
+ version_requirements: *id009
149
+ description: |-
150
+ Delayed_job (or DJ) encapsulates the common pattern of asynchronously executing longer tasks in the background. It is a direct extraction from Shopify where the job table is responsible for a multitude of core tasks.
151
+
152
+ This gem is jmcnevin's fork (http://github.com/jmcnevin/delayed_job).
153
+ email: tobi@leetsoft.com
154
+ executables: []
155
+
156
+ extensions: []
157
+
158
+ extra_rdoc_files:
159
+ - README.textile
160
+ files:
161
+ - .gitignore
162
+ - MIT-LICENSE
163
+ - README.textile
164
+ - Rakefile
165
+ - VERSION
166
+ - benchmarks.rb
167
+ - contrib/delayed_job.monitrc
168
+ - contrib/delayed_job_multiple.monitrc
169
+ - delayed_job.gemspec
170
+ - generators/delayed_job/delayed_job_generator.rb
171
+ - generators/delayed_job/templates/migration.rb
172
+ - init.rb
173
+ - lib/delayed/backend/active_record.rb
174
+ - lib/delayed/backend/base.rb
175
+ - lib/delayed/backend/data_mapper.rb
176
+ - lib/delayed/backend/mongo_mapper.rb
177
+ - lib/delayed/message_sending.rb
178
+ - lib/delayed/performable_method.rb
179
+ - lib/delayed/railtie.rb
180
+ - lib/delayed/recipes.rb
181
+ - lib/delayed/tasks.rb
182
+ - lib/delayed/worker.rb
183
+ - lib/delayed_job.rb
184
+ - rails/init.rb
185
+ - recipes/delayed_job.rb
186
+ - spec/backend/active_record_job_spec.rb
187
+ - spec/backend/data_mapper_job_spec.rb
188
+ - spec/backend/mongo_mapper_job_spec.rb
189
+ - spec/backend/shared_backend_spec.rb
190
+ - spec/delayed_method_spec.rb
191
+ - spec/performable_method_spec.rb
192
+ - spec/sample_jobs.rb
193
+ - spec/setup/active_record.rb
194
+ - spec/setup/data_mapper.rb
195
+ - spec/setup/mongo_mapper.rb
196
+ - spec/spec_helper.rb
197
+ - spec/story_spec.rb
198
+ - spec/worker_spec.rb
199
+ - tasks/jobs.rake
200
+ has_rdoc: true
201
+ homepage: http://github.com/jmcnevin/delayed_job
202
+ licenses: []
203
+
204
+ post_install_message:
205
+ rdoc_options:
206
+ - --main
207
+ - README.textile
208
+ - --inline-source
209
+ - --line-numbers
210
+ require_paths:
211
+ - lib
212
+ required_ruby_version: !ruby/object:Gem::Requirement
213
+ none: false
214
+ requirements:
215
+ - - ">="
216
+ - !ruby/object:Gem::Version
217
+ hash: 3
218
+ segments:
219
+ - 0
220
+ version: "0"
221
+ required_rubygems_version: !ruby/object:Gem::Requirement
222
+ none: false
223
+ requirements:
224
+ - - ">="
225
+ - !ruby/object:Gem::Version
226
+ hash: 3
227
+ segments:
228
+ - 0
229
+ version: "0"
230
+ requirements: []
231
+
232
+ rubyforge_project:
233
+ rubygems_version: 1.3.7
234
+ signing_key:
235
+ specification_version: 3
236
+ summary: Database-backed asynchronous priority queue system -- Extracted from Shopify
237
+ test_files:
238
+ - spec/delayed_method_spec.rb
239
+ - spec/performable_method_spec.rb
240
+ - spec/story_spec.rb
241
+ - spec/worker_spec.rb