delayed_job 1.9.0pre → 2.0.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/README.textile +13 -11
- data/Rakefile +7 -1
- data/VERSION +1 -1
- data/benchmarks.rb +19 -0
- data/delayed_job.gemspec +26 -13
- data/lib/delayed/backend/base.rb +5 -0
- data/lib/delayed/backend/data_mapper.rb +124 -0
- data/spec/backend/active_record_job_spec.rb +7 -9
- data/spec/backend/data_mapper_job_spec.rb +16 -0
- data/spec/backend/mongo_mapper_job_spec.rb +1 -1
- data/spec/backend/shared_backend_spec.rb +2 -0
- data/spec/setup/active_record.rb +5 -5
- data/spec/setup/data_mapper.rb +6 -0
- data/spec/setup/mongo_mapper.rb +4 -2
- data/spec/spec_helper.rb +14 -24
- data/spec/worker_spec.rb +124 -127
- metadata +105 -23
data/README.textile
CHANGED
@@ -40,30 +40,32 @@ After delayed_job is installed, you will need to setup the backend.
|
|
40
40
|
|
41
41
|
h2. Backends
|
42
42
|
|
43
|
-
delayed_job supports multiple backends for storing the job queue.
|
43
|
+
delayed_job supports multiple backends for storing the job queue. There are currently implementations for Active Record, MongoMapper, and DataMapper.
|
44
|
+
|
45
|
+
h3. Active Record
|
46
|
+
|
47
|
+
The default is Active Record, which requires a jobs table.
|
44
48
|
|
45
49
|
<pre>
|
46
|
-
script/generate delayed_job
|
47
|
-
rake db:migrate
|
50
|
+
$ script/generate delayed_job
|
51
|
+
$ rake db:migrate
|
48
52
|
</pre>
|
49
53
|
|
50
|
-
|
54
|
+
h3. MongoMapper
|
51
55
|
|
52
56
|
<pre>
|
53
57
|
# config/initializers/delayed_job.rb
|
54
58
|
Delayed::Worker.backend = :mongo_mapper
|
55
59
|
</pre>
|
56
60
|
|
57
|
-
|
58
|
-
|
59
|
-
If you are upgrading from a previous release, you will need to generate the new @script/delayed_job@:
|
61
|
+
h3. DataMapper
|
60
62
|
|
61
63
|
<pre>
|
62
|
-
|
64
|
+
# config/initializers/delayed_job.rb
|
65
|
+
Delayed::Worker.backend = :data_mapper
|
66
|
+
Delayed::Worker.backend.auto_upgrade!
|
63
67
|
</pre>
|
64
68
|
|
65
|
-
Known Issues: script/delayed_job does not work properly with anything besides the Active Record backend. That will be resolved before the next gem release.
|
66
|
-
|
67
69
|
h2. Queuing Jobs
|
68
70
|
|
69
71
|
Call @#send_later(method, params)@ on any object and it will be processed in the background.
|
@@ -92,7 +94,7 @@ device.deliver
|
|
92
94
|
|
93
95
|
h2. Running Jobs
|
94
96
|
|
95
|
-
@script/delayed_job@ can be used to manage a background process which will start working off jobs.
|
97
|
+
@script/delayed_job@ can be used to manage a background process which will start working off jobs. Make sure you've run `script/generate delayed_job`.
|
96
98
|
|
97
99
|
<pre>
|
98
100
|
$ RAILS_ENV=production script/delayed_job start
|
data/Rakefile
CHANGED
@@ -18,11 +18,17 @@ Jeweler::Tasks.new do |s|
|
|
18
18
|
s.rdoc_options = ["--main", "README.textile", "--inline-source", "--line-numbers"]
|
19
19
|
s.extra_rdoc_files = ["README.textile"]
|
20
20
|
|
21
|
-
s.test_files = Dir['spec
|
21
|
+
s.test_files = Dir['spec/*_spec.rb']
|
22
22
|
|
23
23
|
s.add_dependency "daemons"
|
24
24
|
s.add_development_dependency "rspec"
|
25
25
|
s.add_development_dependency "sqlite3-ruby"
|
26
|
+
s.add_development_dependency "mongo_mapper"
|
27
|
+
s.add_development_dependency "dm-core"
|
28
|
+
s.add_development_dependency "dm-observer"
|
29
|
+
s.add_development_dependency "dm-aggregates"
|
30
|
+
s.add_development_dependency "do_sqlite3"
|
31
|
+
s.add_development_dependency "database_cleaner"
|
26
32
|
end
|
27
33
|
|
28
34
|
require 'spec/rake/spectask'
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.0.0
|
data/benchmarks.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/lib')
|
2
|
+
require 'rubygems'
|
3
|
+
require 'logger'
|
4
|
+
require 'delayed_job'
|
5
|
+
require 'benchmark'
|
6
|
+
|
7
|
+
Delayed::Worker.logger = Logger.new('/dev/null')
|
8
|
+
|
9
|
+
Benchmark.bm(10) do |x|
|
10
|
+
[:active_record, :mongo_mapper, :data_mapper].each do |backend|
|
11
|
+
require "spec/setup/#{backend}"
|
12
|
+
Delayed::Worker.backend = backend
|
13
|
+
|
14
|
+
n = 10000
|
15
|
+
n.times { "foo".send_later :length }
|
16
|
+
|
17
|
+
x.report(backend.to_s) { Delayed::Worker.new(:quiet => true).work_off(n) }
|
18
|
+
end
|
19
|
+
end
|
data/delayed_job.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{delayed_job}
|
8
|
-
s.version = "
|
8
|
+
s.version = "2.0.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Brandon Keepers", "Tobias L\303\274tke"]
|
12
|
-
s.date = %q{2010-03
|
12
|
+
s.date = %q{2010-04-03}
|
13
13
|
s.description = %q{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.
|
14
14
|
|
15
15
|
This gem is collectiveidea's fork (http://github.com/collectiveidea/delayed_job).}
|
@@ -23,6 +23,7 @@ This gem is collectiveidea's fork (http://github.com/collectiveidea/delayed_job)
|
|
23
23
|
"README.textile",
|
24
24
|
"Rakefile",
|
25
25
|
"VERSION",
|
26
|
+
"benchmarks.rb",
|
26
27
|
"contrib/delayed_job.monitrc",
|
27
28
|
"delayed_job.gemspec",
|
28
29
|
"generators/delayed_job/delayed_job_generator.rb",
|
@@ -30,6 +31,7 @@ This gem is collectiveidea's fork (http://github.com/collectiveidea/delayed_job)
|
|
30
31
|
"generators/delayed_job/templates/script",
|
31
32
|
"lib/delayed/backend/active_record.rb",
|
32
33
|
"lib/delayed/backend/base.rb",
|
34
|
+
"lib/delayed/backend/data_mapper.rb",
|
33
35
|
"lib/delayed/backend/mongo_mapper.rb",
|
34
36
|
"lib/delayed/command.rb",
|
35
37
|
"lib/delayed/message_sending.rb",
|
@@ -41,12 +43,14 @@ This gem is collectiveidea's fork (http://github.com/collectiveidea/delayed_job)
|
|
41
43
|
"rails/init.rb",
|
42
44
|
"recipes/delayed_job.rb",
|
43
45
|
"spec/backend/active_record_job_spec.rb",
|
46
|
+
"spec/backend/data_mapper_job_spec.rb",
|
44
47
|
"spec/backend/mongo_mapper_job_spec.rb",
|
45
48
|
"spec/backend/shared_backend_spec.rb",
|
46
49
|
"spec/delayed_method_spec.rb",
|
47
50
|
"spec/performable_method_spec.rb",
|
48
51
|
"spec/sample_jobs.rb",
|
49
52
|
"spec/setup/active_record.rb",
|
53
|
+
"spec/setup/data_mapper.rb",
|
50
54
|
"spec/setup/mongo_mapper.rb",
|
51
55
|
"spec/spec_helper.rb",
|
52
56
|
"spec/story_spec.rb",
|
@@ -56,20 +60,11 @@ This gem is collectiveidea's fork (http://github.com/collectiveidea/delayed_job)
|
|
56
60
|
s.homepage = %q{http://github.com/collectiveidea/delayed_job}
|
57
61
|
s.rdoc_options = ["--main", "README.textile", "--inline-source", "--line-numbers"]
|
58
62
|
s.require_paths = ["lib"]
|
59
|
-
s.rubygems_version = %q{1.3.
|
63
|
+
s.rubygems_version = %q{1.3.6}
|
60
64
|
s.summary = %q{Database-backed asynchronous priority queue system -- Extracted from Shopify}
|
61
65
|
s.test_files = [
|
62
|
-
"spec/
|
63
|
-
"spec/backend/active_record_job_spec.rb",
|
64
|
-
"spec/backend/mongo_mapper_job_spec.rb",
|
65
|
-
"spec/backend/shared_backend_spec.rb",
|
66
|
-
"spec/delayed_method_spec.rb",
|
66
|
+
"spec/delayed_method_spec.rb",
|
67
67
|
"spec/performable_method_spec.rb",
|
68
|
-
"spec/sample_jobs.rb",
|
69
|
-
"spec/setup",
|
70
|
-
"spec/setup/active_record.rb",
|
71
|
-
"spec/setup/mongo_mapper.rb",
|
72
|
-
"spec/spec_helper.rb",
|
73
68
|
"spec/story_spec.rb",
|
74
69
|
"spec/worker_spec.rb"
|
75
70
|
]
|
@@ -82,15 +77,33 @@ This gem is collectiveidea's fork (http://github.com/collectiveidea/delayed_job)
|
|
82
77
|
s.add_runtime_dependency(%q<daemons>, [">= 0"])
|
83
78
|
s.add_development_dependency(%q<rspec>, [">= 0"])
|
84
79
|
s.add_development_dependency(%q<sqlite3-ruby>, [">= 0"])
|
80
|
+
s.add_development_dependency(%q<mongo_mapper>, [">= 0"])
|
81
|
+
s.add_development_dependency(%q<dm-core>, [">= 0"])
|
82
|
+
s.add_development_dependency(%q<dm-observer>, [">= 0"])
|
83
|
+
s.add_development_dependency(%q<dm-aggregates>, [">= 0"])
|
84
|
+
s.add_development_dependency(%q<do_sqlite3>, [">= 0"])
|
85
|
+
s.add_development_dependency(%q<database_cleaner>, [">= 0"])
|
85
86
|
else
|
86
87
|
s.add_dependency(%q<daemons>, [">= 0"])
|
87
88
|
s.add_dependency(%q<rspec>, [">= 0"])
|
88
89
|
s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
|
90
|
+
s.add_dependency(%q<mongo_mapper>, [">= 0"])
|
91
|
+
s.add_dependency(%q<dm-core>, [">= 0"])
|
92
|
+
s.add_dependency(%q<dm-observer>, [">= 0"])
|
93
|
+
s.add_dependency(%q<dm-aggregates>, [">= 0"])
|
94
|
+
s.add_dependency(%q<do_sqlite3>, [">= 0"])
|
95
|
+
s.add_dependency(%q<database_cleaner>, [">= 0"])
|
89
96
|
end
|
90
97
|
else
|
91
98
|
s.add_dependency(%q<daemons>, [">= 0"])
|
92
99
|
s.add_dependency(%q<rspec>, [">= 0"])
|
93
100
|
s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
|
101
|
+
s.add_dependency(%q<mongo_mapper>, [">= 0"])
|
102
|
+
s.add_dependency(%q<dm-core>, [">= 0"])
|
103
|
+
s.add_dependency(%q<dm-observer>, [">= 0"])
|
104
|
+
s.add_dependency(%q<dm-aggregates>, [">= 0"])
|
105
|
+
s.add_dependency(%q<do_sqlite3>, [">= 0"])
|
106
|
+
s.add_dependency(%q<database_cleaner>, [">= 0"])
|
94
107
|
end
|
95
108
|
end
|
96
109
|
|
data/lib/delayed/backend/base.rb
CHANGED
@@ -28,6 +28,11 @@ module Delayed
|
|
28
28
|
# Hook method that is called after a new worker is forked
|
29
29
|
def after_fork
|
30
30
|
end
|
31
|
+
|
32
|
+
def work_off(num = 100)
|
33
|
+
warn "[DEPRECATION] `Delayed::Job.work_off` is deprecated. Use `Delayed::Worker.new.work_off instead."
|
34
|
+
Delayed::Worker.new.work_off(num)
|
35
|
+
end
|
31
36
|
end
|
32
37
|
|
33
38
|
ParseObjectFromYaml = /\!ruby\/\w+\:([^\s]+)/
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'dm-observer'
|
3
|
+
require 'dm-aggregates'
|
4
|
+
|
5
|
+
module DataMapper
|
6
|
+
module Resource
|
7
|
+
module ClassMethods
|
8
|
+
def load_for_delayed_job(id)
|
9
|
+
find!(id)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module InstanceMethods
|
14
|
+
def dump_for_delayed_job
|
15
|
+
"#{self.class};#{id}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Delayed
|
22
|
+
module Backend
|
23
|
+
module DataMapper
|
24
|
+
class Job
|
25
|
+
include ::DataMapper::Resource
|
26
|
+
include Delayed::Backend::Base
|
27
|
+
|
28
|
+
storage_names[:default] = 'delayed_jobs'
|
29
|
+
|
30
|
+
property :id, Serial
|
31
|
+
property :priority, Integer, :default => 0
|
32
|
+
property :attempts, Integer, :default => 0
|
33
|
+
property :handler, String
|
34
|
+
property :run_at, Time
|
35
|
+
property :locked_at, Time
|
36
|
+
property :locked_by, String
|
37
|
+
property :failed_at, Time
|
38
|
+
property :last_error, String
|
39
|
+
|
40
|
+
def self.db_time_now
|
41
|
+
Time.now
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time)
|
45
|
+
|
46
|
+
simple_conditions = { :run_at.lte => db_time_now, :limit => limit, :failed_at => nil, :order => [:priority.asc, :run_at.asc] }
|
47
|
+
|
48
|
+
# respect priorities
|
49
|
+
simple_conditions[:priority.gte] = Worker.min_priority if Worker.min_priority
|
50
|
+
simple_conditions[:priority.lte] = Worker.max_priority if Worker.max_priority
|
51
|
+
|
52
|
+
# lockable
|
53
|
+
lockable = (
|
54
|
+
# not locked or past the max time
|
55
|
+
( all(:locked_at => nil ) | all(:locked_at.lt => db_time_now - max_run_time)) |
|
56
|
+
|
57
|
+
# OR locked by our worker
|
58
|
+
all(:locked_by => worker_name))
|
59
|
+
|
60
|
+
# plus some other boring junk
|
61
|
+
(lockable).all( simple_conditions )
|
62
|
+
end
|
63
|
+
|
64
|
+
# When a worker is exiting, make sure we don't have any locked jobs.
|
65
|
+
def self.clear_locks!(worker_name)
|
66
|
+
all(:locked_by => worker_name).update(:locked_at => nil, :locked_by => nil)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Lock this job for this worker.
|
70
|
+
# Returns true if we have the lock, false otherwise.
|
71
|
+
def lock_exclusively!(max_run_time, worker = worker_name)
|
72
|
+
now = self.class.db_time_now
|
73
|
+
overtime = now - max_run_time
|
74
|
+
|
75
|
+
# FIXME - this is a bit gross
|
76
|
+
# DM doesn't give us the number of rows affected by a collection update
|
77
|
+
# so we have to circumvent some niceness in DM::Collection here
|
78
|
+
collection = locked_by != worker ?
|
79
|
+
(self.class.all(:id => id, :run_at.lte => now) & ( self.class.all(:locked_at => nil) | self.class.all(:locked_at.lt => overtime) ) ) :
|
80
|
+
self.class.all(:id => id, :locked_by => worker)
|
81
|
+
|
82
|
+
attributes = collection.model.new(:locked_at => now, :locked_by => worker).dirty_attributes
|
83
|
+
affected_rows = self.repository.update(attributes, collection)
|
84
|
+
|
85
|
+
if affected_rows == 1
|
86
|
+
self.locked_at = now
|
87
|
+
self.locked_by = worker
|
88
|
+
return true
|
89
|
+
else
|
90
|
+
return false
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# these are common to the other backends, so we provide an implementation
|
95
|
+
def self.delete_all
|
96
|
+
Delayed::Job.auto_migrate!
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.find id
|
100
|
+
get id
|
101
|
+
end
|
102
|
+
|
103
|
+
def update_attributes(attributes)
|
104
|
+
attributes.each do |k,v|
|
105
|
+
self[k] = v
|
106
|
+
end
|
107
|
+
self.save
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
class JobObserver
|
114
|
+
include ::DataMapper::Observer
|
115
|
+
|
116
|
+
observe Job
|
117
|
+
|
118
|
+
before :save do
|
119
|
+
self.run_at ||= self.class.db_time_now
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -1,15 +1,14 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'backend/shared_backend_spec'
|
2
3
|
require 'delayed/backend/active_record'
|
3
4
|
|
4
|
-
describe Delayed::Job do
|
5
|
+
describe Delayed::Backend::ActiveRecord::Job do
|
5
6
|
before(:all) do
|
6
|
-
@backend = Delayed::Job
|
7
|
+
@backend = Delayed::Backend::ActiveRecord::Job
|
7
8
|
end
|
8
9
|
|
9
10
|
before(:each) do
|
10
|
-
Delayed::
|
11
|
-
Delayed::Worker.min_priority = nil
|
12
|
-
Delayed::Job.delete_all
|
11
|
+
Delayed::Backend::ActiveRecord::Job.delete_all
|
13
12
|
SimpleJob.runs = 0
|
14
13
|
end
|
15
14
|
|
@@ -28,14 +27,13 @@ describe Delayed::Job do
|
|
28
27
|
it "should return UTC time if that is the AR default" do
|
29
28
|
Time.zone = nil
|
30
29
|
ActiveRecord::Base.default_timezone = :utc
|
31
|
-
Delayed::Job.db_time_now.zone.should == 'UTC'
|
30
|
+
Delayed::Backend::ActiveRecord::Job.db_time_now.zone.should == 'UTC'
|
32
31
|
end
|
33
32
|
|
34
33
|
it "should return local time if that is the AR default" do
|
35
34
|
Time.zone = 'Central Time (US & Canada)'
|
36
35
|
ActiveRecord::Base.default_timezone = :local
|
37
|
-
%w(CST CDT).should include(Delayed::Job.db_time_now.zone)
|
36
|
+
%w(CST CDT).should include(Delayed::Backend::ActiveRecord::Job.db_time_now.zone)
|
38
37
|
end
|
39
|
-
end
|
40
|
-
|
38
|
+
end
|
41
39
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'backend/shared_backend_spec'
|
3
|
+
require 'delayed/backend/data_mapper'
|
4
|
+
|
5
|
+
describe Delayed::Backend::DataMapper::Job do
|
6
|
+
before(:all) do
|
7
|
+
@backend = Delayed::Backend::DataMapper::Job
|
8
|
+
end
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
# reset database before each example is run
|
12
|
+
DataMapper.auto_migrate!
|
13
|
+
end
|
14
|
+
|
15
|
+
it_should_behave_like 'a backend'
|
16
|
+
end
|
data/spec/setup/active_record.rb
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
|
3
3
|
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
|
4
|
-
ActiveRecord::Base.logger =
|
4
|
+
ActiveRecord::Base.logger = Delayed::Worker.logger
|
5
5
|
ActiveRecord::Migration.verbose = false
|
6
6
|
|
7
7
|
ActiveRecord::Schema.define do
|
8
|
-
|
9
8
|
create_table :delayed_jobs, :force => true do |table|
|
10
9
|
table.integer :priority, :default => 0
|
11
10
|
table.integer :attempts, :default => 0
|
12
11
|
table.text :handler
|
13
|
-
table.
|
12
|
+
table.text :last_error
|
14
13
|
table.datetime :run_at
|
15
14
|
table.datetime :locked_at
|
16
|
-
table.string :locked_by
|
17
15
|
table.datetime :failed_at
|
16
|
+
table.string :locked_by
|
18
17
|
table.timestamps
|
19
18
|
end
|
20
19
|
|
20
|
+
add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority'
|
21
|
+
|
21
22
|
create_table :stories, :force => true do |table|
|
22
23
|
table.string :text
|
23
24
|
end
|
24
|
-
|
25
25
|
end
|
26
26
|
|
27
27
|
# Purely useful for test cases...
|
data/spec/setup/mongo_mapper.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
require 'mongo_mapper'
|
2
|
+
|
3
|
+
MongoMapper.connection = Mongo::Connection.new nil, nil, :logger => Delayed::Worker.logger
|
2
4
|
MongoMapper.database = 'delayed_job'
|
3
5
|
|
4
6
|
unless defined?(Story)
|
@@ -7,7 +9,7 @@ unless defined?(Story)
|
|
7
9
|
def tell; text; end
|
8
10
|
def whatever(n, _); tell*n; end
|
9
11
|
def self.count; end
|
10
|
-
|
12
|
+
|
11
13
|
handle_asynchronously :whatever
|
12
14
|
end
|
13
15
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,32 +4,22 @@ require 'rubygems'
|
|
4
4
|
require 'spec'
|
5
5
|
require 'logger'
|
6
6
|
|
7
|
-
backends_available = []
|
8
|
-
%w(active_record mongo_mapper).each do |backend|
|
9
|
-
begin
|
10
|
-
require backend
|
11
|
-
backends_available << backend
|
12
|
-
rescue LoadError => e
|
13
|
-
$stderr.puts "The backend '#{backend}' is not available. Skipping tests"
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
if backends_available.empty?
|
18
|
-
raise LoadError, "Cannot run delayed_job specs. No backends available"
|
19
|
-
end
|
20
|
-
|
21
7
|
require 'delayed_job'
|
22
8
|
require 'sample_jobs'
|
23
|
-
require 'backend/shared_backend_spec'
|
24
9
|
|
25
|
-
|
26
|
-
Delayed::Worker.logger = DELAYED_JOB_LOGGER
|
10
|
+
Delayed::Worker.logger = Logger.new('/tmp/dj.log')
|
27
11
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
12
|
+
# determine the available backends
|
13
|
+
BACKENDS = []
|
14
|
+
Dir.glob("#{File.dirname(__FILE__)}/setup/*.rb") do |backend|
|
15
|
+
begin
|
16
|
+
backend = File.basename(backend, '.rb')
|
17
|
+
require "setup/#{backend}"
|
18
|
+
require "backend/#{backend}_job_spec"
|
19
|
+
BACKENDS << backend.to_sym
|
20
|
+
rescue LoadError
|
21
|
+
puts "Unable to load #{backend} backend! #{$!}"
|
22
|
+
end
|
35
23
|
end
|
24
|
+
|
25
|
+
Delayed::Worker.backend = BACKENDS.first
|
data/spec/worker_spec.rb
CHANGED
@@ -5,21 +5,6 @@ describe Delayed::Worker do
|
|
5
5
|
Delayed::Job.create(opts.merge(:payload_object => SimpleJob.new))
|
6
6
|
end
|
7
7
|
|
8
|
-
before(:all) do
|
9
|
-
Delayed::Worker.send :public, :work_off
|
10
|
-
end
|
11
|
-
|
12
|
-
before(:each) do
|
13
|
-
# Make sure backend is set to active record
|
14
|
-
Delayed::Worker.backend = DEFAULT_BACKEND
|
15
|
-
|
16
|
-
@worker = Delayed::Worker.new(:max_priority => nil, :min_priority => nil, :quiet => true)
|
17
|
-
|
18
|
-
Delayed::Job.delete_all
|
19
|
-
|
20
|
-
SimpleJob.runs = 0
|
21
|
-
end
|
22
|
-
|
23
8
|
describe "backend=" do
|
24
9
|
it "should set the Delayed::Job constant to the backend" do
|
25
10
|
@clazz = Class.new
|
@@ -34,145 +19,157 @@ describe Delayed::Worker do
|
|
34
19
|
end
|
35
20
|
end
|
36
21
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
Delayed::
|
42
|
-
|
43
|
-
@worker.
|
44
|
-
|
45
|
-
|
46
|
-
ensure
|
47
|
-
Delayed::Worker.max_run_time = old_max_run_time
|
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
|
48
31
|
end
|
49
|
-
end
|
50
|
-
end
|
51
32
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
56
52
|
|
57
|
-
|
58
|
-
|
53
|
+
it "should only work_off jobs that are >= min_priority" do
|
54
|
+
SimpleJob.runs.should == 0
|
59
55
|
|
60
|
-
|
61
|
-
|
62
|
-
|
56
|
+
job_create(:priority => -10)
|
57
|
+
job_create(:priority => 0)
|
58
|
+
@worker.work_off
|
63
59
|
|
64
|
-
|
65
|
-
|
60
|
+
SimpleJob.runs.should == 1
|
61
|
+
end
|
66
62
|
|
67
|
-
|
68
|
-
|
63
|
+
it "should only work_off jobs that are <= max_priority" do
|
64
|
+
SimpleJob.runs.should == 0
|
69
65
|
|
70
|
-
|
71
|
-
|
66
|
+
job_create(:priority => 10)
|
67
|
+
job_create(:priority => 0)
|
72
68
|
|
73
|
-
|
69
|
+
@worker.work_off
|
74
70
|
|
75
|
-
|
76
|
-
|
77
|
-
|
71
|
+
SimpleJob.runs.should == 1
|
72
|
+
end
|
73
|
+
end
|
78
74
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
75
|
+
context "while running with locked and expired jobs" do
|
76
|
+
before(:each) do
|
77
|
+
@worker.name = 'worker1'
|
78
|
+
end
|
83
79
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
88
84
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
93
89
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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
|
99
95
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
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
|
105
101
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
102
|
+
describe "failed jobs" do
|
103
|
+
before do
|
104
|
+
# reset defaults
|
105
|
+
Delayed::Worker.destroy_failed_jobs = true
|
106
|
+
Delayed::Worker.max_attempts = 25
|
111
107
|
|
112
|
-
|
113
|
-
|
108
|
+
@job = Delayed::Job.enqueue ErrorJob.new
|
109
|
+
end
|
114
110
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
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
|
125
121
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
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
|
136
132
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
133
|
+
context "reschedule" do
|
134
|
+
before do
|
135
|
+
@job = Delayed::Job.create :payload_object => SimpleJob.new
|
136
|
+
end
|
141
137
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
138
|
+
context "and we want to destroy jobs" do
|
139
|
+
before do
|
140
|
+
Delayed::Worker.destroy_failed_jobs = true
|
141
|
+
end
|
146
142
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
143
|
+
it "should be destroyed if it failed more than Worker.max_attempts times" do
|
144
|
+
@job.should_receive(:destroy)
|
145
|
+
Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }
|
146
|
+
end
|
151
147
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
148
|
+
it "should not be destroyed if failed fewer than Worker.max_attempts times" do
|
149
|
+
@job.should_not_receive(:destroy)
|
150
|
+
(Delayed::Worker.max_attempts - 1).times { @worker.reschedule(@job) }
|
151
|
+
end
|
152
|
+
end
|
157
153
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
154
|
+
context "and we don't want to destroy jobs" do
|
155
|
+
before do
|
156
|
+
Delayed::Worker.destroy_failed_jobs = false
|
157
|
+
end
|
162
158
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
159
|
+
it "should be failed if it failed more than Worker.max_attempts times" do
|
160
|
+
@job.reload.failed_at.should == nil
|
161
|
+
Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }
|
162
|
+
@job.reload.failed_at.should_not == nil
|
163
|
+
end
|
168
164
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
165
|
+
it "should not be failed if it failed fewer than Worker.max_attempts times" do
|
166
|
+
(Delayed::Worker.max_attempts - 1).times { @worker.reschedule(@job) }
|
167
|
+
@job.reload.failed_at.should == nil
|
168
|
+
end
|
173
169
|
|
170
|
+
end
|
171
|
+
end
|
174
172
|
end
|
175
173
|
end
|
176
174
|
|
177
|
-
|
178
175
|
end
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delayed_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 2
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
version: 2.0.0
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- Brandon Keepers
|
@@ -10,39 +15,117 @@ autorequire:
|
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
17
|
|
13
|
-
date: 2010-03
|
18
|
+
date: 2010-04-03 00:00:00 -04:00
|
14
19
|
default_executable:
|
15
20
|
dependencies:
|
16
21
|
- !ruby/object:Gem::Dependency
|
17
22
|
name: daemons
|
18
|
-
|
19
|
-
|
20
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
21
25
|
requirements:
|
22
26
|
- - ">="
|
23
27
|
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
24
30
|
version: "0"
|
25
|
-
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
26
33
|
- !ruby/object:Gem::Dependency
|
27
34
|
name: rspec
|
28
|
-
|
29
|
-
|
30
|
-
version_requirements: !ruby/object:Gem::Requirement
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
37
|
requirements:
|
32
38
|
- - ">="
|
33
39
|
- !ruby/object:Gem::Version
|
40
|
+
segments:
|
41
|
+
- 0
|
34
42
|
version: "0"
|
35
|
-
|
43
|
+
type: :development
|
44
|
+
version_requirements: *id002
|
36
45
|
- !ruby/object:Gem::Dependency
|
37
46
|
name: sqlite3-ruby
|
47
|
+
prerelease: false
|
48
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
segments:
|
53
|
+
- 0
|
54
|
+
version: "0"
|
55
|
+
type: :development
|
56
|
+
version_requirements: *id003
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: mongo_mapper
|
59
|
+
prerelease: false
|
60
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
type: :development
|
68
|
+
version_requirements: *id004
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: dm-core
|
71
|
+
prerelease: false
|
72
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
version: "0"
|
38
79
|
type: :development
|
39
|
-
|
40
|
-
|
80
|
+
version_requirements: *id005
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: dm-observer
|
83
|
+
prerelease: false
|
84
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
41
85
|
requirements:
|
42
86
|
- - ">="
|
43
87
|
- !ruby/object:Gem::Version
|
88
|
+
segments:
|
89
|
+
- 0
|
44
90
|
version: "0"
|
45
|
-
|
91
|
+
type: :development
|
92
|
+
version_requirements: *id006
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: dm-aggregates
|
95
|
+
prerelease: false
|
96
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
version: "0"
|
103
|
+
type: :development
|
104
|
+
version_requirements: *id007
|
105
|
+
- !ruby/object:Gem::Dependency
|
106
|
+
name: do_sqlite3
|
107
|
+
prerelease: false
|
108
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
segments:
|
113
|
+
- 0
|
114
|
+
version: "0"
|
115
|
+
type: :development
|
116
|
+
version_requirements: *id008
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: database_cleaner
|
119
|
+
prerelease: false
|
120
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
segments:
|
125
|
+
- 0
|
126
|
+
version: "0"
|
127
|
+
type: :development
|
128
|
+
version_requirements: *id009
|
46
129
|
description: |-
|
47
130
|
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.
|
48
131
|
|
@@ -60,6 +143,7 @@ files:
|
|
60
143
|
- README.textile
|
61
144
|
- Rakefile
|
62
145
|
- VERSION
|
146
|
+
- benchmarks.rb
|
63
147
|
- contrib/delayed_job.monitrc
|
64
148
|
- delayed_job.gemspec
|
65
149
|
- generators/delayed_job/delayed_job_generator.rb
|
@@ -67,6 +151,7 @@ files:
|
|
67
151
|
- generators/delayed_job/templates/script
|
68
152
|
- lib/delayed/backend/active_record.rb
|
69
153
|
- lib/delayed/backend/base.rb
|
154
|
+
- lib/delayed/backend/data_mapper.rb
|
70
155
|
- lib/delayed/backend/mongo_mapper.rb
|
71
156
|
- lib/delayed/command.rb
|
72
157
|
- lib/delayed/message_sending.rb
|
@@ -78,12 +163,14 @@ files:
|
|
78
163
|
- rails/init.rb
|
79
164
|
- recipes/delayed_job.rb
|
80
165
|
- spec/backend/active_record_job_spec.rb
|
166
|
+
- spec/backend/data_mapper_job_spec.rb
|
81
167
|
- spec/backend/mongo_mapper_job_spec.rb
|
82
168
|
- spec/backend/shared_backend_spec.rb
|
83
169
|
- spec/delayed_method_spec.rb
|
84
170
|
- spec/performable_method_spec.rb
|
85
171
|
- spec/sample_jobs.rb
|
86
172
|
- spec/setup/active_record.rb
|
173
|
+
- spec/setup/data_mapper.rb
|
87
174
|
- spec/setup/mongo_mapper.rb
|
88
175
|
- spec/spec_helper.rb
|
89
176
|
- spec/story_spec.rb
|
@@ -105,30 +192,25 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
105
192
|
requirements:
|
106
193
|
- - ">="
|
107
194
|
- !ruby/object:Gem::Version
|
195
|
+
segments:
|
196
|
+
- 0
|
108
197
|
version: "0"
|
109
|
-
version:
|
110
198
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
199
|
requirements:
|
112
200
|
- - ">="
|
113
201
|
- !ruby/object:Gem::Version
|
202
|
+
segments:
|
203
|
+
- 0
|
114
204
|
version: "0"
|
115
|
-
version:
|
116
205
|
requirements: []
|
117
206
|
|
118
207
|
rubyforge_project:
|
119
|
-
rubygems_version: 1.3.
|
208
|
+
rubygems_version: 1.3.6
|
120
209
|
signing_key:
|
121
210
|
specification_version: 3
|
122
211
|
summary: Database-backed asynchronous priority queue system -- Extracted from Shopify
|
123
212
|
test_files:
|
124
|
-
- spec/backend/active_record_job_spec.rb
|
125
|
-
- spec/backend/mongo_mapper_job_spec.rb
|
126
|
-
- spec/backend/shared_backend_spec.rb
|
127
213
|
- spec/delayed_method_spec.rb
|
128
214
|
- spec/performable_method_spec.rb
|
129
|
-
- spec/sample_jobs.rb
|
130
|
-
- spec/setup/active_record.rb
|
131
|
-
- spec/setup/mongo_mapper.rb
|
132
|
-
- spec/spec_helper.rb
|
133
215
|
- spec/story_spec.rb
|
134
216
|
- spec/worker_spec.rb
|