jmcnevin-delayed_job 2.0.4

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 (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