blaxter-delayed_job 1.7.0 → 1.7.99
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.textile +27 -19
- data/Rakefile +38 -0
- data/delayed_job.gemspec +51 -35
- data/generators/delayed_job/delayed_job_generator.rb +11 -0
- data/generators/delayed_job/templates/migration.rb +21 -0
- data/lib/delayed/job.rb +23 -8
- data/lib/delayed/worker.rb +1 -0
- data/spec/database.rb +12 -6
- data/spec/job_spec.rb +107 -4
- metadata +16 -6
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.gem
|
data/README.textile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
h1. Delayed::Job
|
2
2
|
|
3
|
-
|
3
|
+
Delayed_job (or DJ) encapsulates the common pattern of asynchronously executing longer tasks in the background.
|
4
4
|
|
5
5
|
It is a direct extraction from Shopify where the job table is responsible for a multitude of core tasks. Amongst those tasks are:
|
6
6
|
|
@@ -15,39 +15,43 @@ It is a direct extraction from Shopify where the job table is responsible for a
|
|
15
15
|
h2. Setup
|
16
16
|
|
17
17
|
The library evolves around a delayed_jobs table which looks as follows:
|
18
|
-
|
19
|
-
create_table :delayed_jobs, :force => true do |table|
|
18
|
+
<pre><code>create_table :delayed_jobs, :force => true do |table|
|
20
19
|
table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue
|
21
20
|
table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually.
|
22
21
|
table.text :handler # YAML-encoded string of the object that will do work
|
22
|
+
table.string :job_type # Class name of the job object, for type-specific workers
|
23
23
|
table.string :last_error # reason for last failure (See Note below)
|
24
24
|
table.datetime :run_at # When to run. Could be Time.now for immediately, or sometime in the future.
|
25
25
|
table.datetime :locked_at # Set when a client is working on this object
|
26
26
|
table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)
|
27
27
|
table.string :locked_by # Who is working on this object (if locked)
|
28
|
+
table.datetime :finished_at # Used for statiscics / monitoring
|
28
29
|
table.timestamps
|
29
30
|
end
|
30
|
-
|
31
|
+
</code></pre>
|
31
32
|
On failure, the job is scheduled again in 5 seconds + N ** 4, where N is the number of retries.
|
32
33
|
|
33
|
-
The default MAX_ATTEMPTS is 25
|
34
|
-
|
34
|
+
The default MAX_ATTEMPTS is 25- jobs can override this value by responding to :max_attempts. After this, the job
|
35
|
+
either deleted (default), or left in the database with "failed_at" set. With the default of 25 attempts,
|
36
|
+
the last retry will be 20 days later, with the last interval being almost 100 hours.
|
35
37
|
|
36
38
|
The default MAX_RUN_TIME is 4.hours. If your job takes longer than that, another computer could pick it up. It's up to you to
|
37
39
|
make sure your job doesn't exceed this time. You should set this to the longest time you think the job could take.
|
38
40
|
|
39
|
-
By default, it will delete failed jobs
|
40
|
-
Delayed::Job.destroy_failed_jobs = false
|
41
|
+
By default, it will delete failed jobs. If you want to keep failed jobs, set
|
42
|
+
@Delayed::Job.destroy_failed_jobs = false@. The failed jobs will be marked with non-null failed_at.
|
41
43
|
|
42
|
-
|
44
|
+
Same thing for successful jobs. They're deleted by default and, to keep them, set @Delayed::Job.destroy_successful_jobs = false@. They will be marked with finished_at. This is useful for gathering statistics like how long jobs took between entering the queue (created_at) and being finished (finished_at).
|
43
45
|
|
44
|
-
|
46
|
+
Here is an example of changing job parameters in Rails:
|
47
|
+
<pre><code># config/initializers/delayed_job_config.rb
|
45
48
|
Delayed::Job.destroy_failed_jobs = false
|
49
|
+
Delayed::Job.destroy_successful_jobs = false
|
46
50
|
silence_warnings do
|
47
51
|
Delayed::Job.const_set("MAX_ATTEMPTS", 3)
|
48
52
|
Delayed::Job.const_set("MAX_RUN_TIME", 5.minutes)
|
49
53
|
end
|
50
|
-
|
54
|
+
</code></pre>
|
51
55
|
Note: If your error messages are long, consider changing last_error field to a :text instead of a :string (255 character limit).
|
52
56
|
|
53
57
|
|
@@ -55,20 +59,18 @@ h2. Usage
|
|
55
59
|
|
56
60
|
Jobs are simple ruby objects with a method called perform. Any object which responds to perform can be stuffed into the jobs table.
|
57
61
|
Job objects are serialized to yaml so that they can later be resurrected by the job runner.
|
58
|
-
|
59
|
-
class NewsletterJob < Struct.new(:text, :emails)
|
62
|
+
<pre><code>class NewsletterJob < Struct.new(:text, :emails)
|
60
63
|
def perform
|
61
64
|
emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
|
62
65
|
end
|
63
66
|
end
|
64
67
|
|
65
68
|
Delayed::Job.enqueue NewsletterJob.new('lorem ipsum...', Customers.find(:all).collect(&:email))
|
66
|
-
|
69
|
+
</code></pre>
|
67
70
|
There is also a second way to get jobs in the queue: send_later.
|
68
71
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
+
<pre><code>BatchImporter.new(Shop.find(1)).send_later(:import_massive_csv, massive_csv)
|
73
|
+
</code></pre>
|
72
74
|
|
73
75
|
This will simply create a Delayed::PerformableMethod job in the jobs table which serializes all the parameters you pass to it. There are some special smarts for active record objects
|
74
76
|
which are stored as their text representation and loaded from the database fresh when the job is actually run later.
|
@@ -80,8 +82,7 @@ You can invoke @rake jobs:work@ which will start working off jobs. You can cance
|
|
80
82
|
|
81
83
|
You can also run by writing a simple @script/job_runner@, and invoking it externally:
|
82
84
|
|
83
|
-
<pre><code
|
84
|
-
#!/usr/bin/env ruby
|
85
|
+
<pre><code>#!/usr/bin/env ruby
|
85
86
|
require File.dirname(__FILE__) + '/../config/environment'
|
86
87
|
|
87
88
|
Delayed::Worker.new.start
|
@@ -93,6 +94,13 @@ Keep in mind that each worker will check the database at least every 5 seconds.
|
|
93
94
|
|
94
95
|
Note: The rake task will exit if the database has any network connectivity problems.
|
95
96
|
|
97
|
+
If you only want to run specific types of jobs in a given worker, include them when initializing the worker:
|
98
|
+
|
99
|
+
<pre><code>
|
100
|
+
Delayed::Worker.new(:job_types => "SimpleJob").start
|
101
|
+
Delayed::Worker.new(:job_types => ["SimpleJob", "NewsletterJob"]).start
|
102
|
+
</pre></code>
|
103
|
+
|
96
104
|
h3. Cleaning up
|
97
105
|
|
98
106
|
You can invoke @rake jobs:clear@ to delete all jobs in the queue.
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
begin
|
3
|
+
require 'jeweler'
|
4
|
+
|
5
|
+
Jeweler::Tasks.new do |s|
|
6
|
+
s.name = "delayed_job"
|
7
|
+
s.summary = "Database-backed asynchronous priority queue system -- Extracted from Shopify"
|
8
|
+
s.email = "tobi@leetsoft.com"
|
9
|
+
s.homepage = "http://github.com/tobi/delayed_job/tree/master"
|
10
|
+
s.description = "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."
|
11
|
+
s.authors = ["Tobias Lütke"]
|
12
|
+
|
13
|
+
s.has_rdoc = true
|
14
|
+
s.rdoc_options = ["--main", "README.textile", "--inline-source", "--line-numbers"]
|
15
|
+
s.extra_rdoc_files = ["README.textile"]
|
16
|
+
|
17
|
+
s.test_files = Dir['spec/**/*']
|
18
|
+
end
|
19
|
+
|
20
|
+
rescue LoadError
|
21
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'spec/rake/spectask'
|
26
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
27
|
+
spec.libs << 'lib' << 'spec'
|
28
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
29
|
+
end
|
30
|
+
|
31
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
32
|
+
spec.libs << 'lib' << 'spec'
|
33
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
34
|
+
spec.rcov = true
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
task :default => :spec
|
data/delayed_job.gemspec
CHANGED
@@ -1,41 +1,57 @@
|
|
1
|
-
#
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
|
-
s.name
|
5
|
-
s.version
|
6
|
-
s.date = "2008-11-28"
|
7
|
-
s.summary = "Database-backed asynchronous priority queue system -- Extracted from Shopify"
|
8
|
-
s.email = "tobi@leetsoft.com"
|
9
|
-
s.homepage = "http://github.com/tobi/delayed_job/tree/master"
|
10
|
-
s.description = "Delated_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."
|
11
|
-
s.authors = ["Tobias Lütke"]
|
4
|
+
s.name = %q{delayed_job}
|
5
|
+
s.version = "1.7.99"
|
12
6
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
s.
|
18
|
-
s.
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Tobias L\303\274tke"]
|
9
|
+
s.date = %q{2009-08-06}
|
10
|
+
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.}
|
11
|
+
s.email = %q{tobi@leetsoft.com}
|
12
|
+
s.extra_rdoc_files = [
|
13
|
+
"README.textile"
|
14
|
+
]
|
15
|
+
s.files = [
|
16
|
+
".gitignore",
|
17
|
+
"MIT-LICENSE",
|
18
|
+
"README.textile",
|
19
|
+
"Rakefile",
|
20
|
+
"delayed_job.gemspec",
|
21
|
+
"generators/delayed_job/delayed_job_generator.rb",
|
22
|
+
"generators/delayed_job/templates/migration.rb",
|
23
|
+
"init.rb",
|
24
|
+
"lib/delayed/job.rb",
|
25
|
+
"lib/delayed/message_sending.rb",
|
26
|
+
"lib/delayed/performable_method.rb",
|
27
|
+
"lib/delayed/worker.rb",
|
28
|
+
"lib/delayed_job.rb",
|
29
|
+
"spec/database.rb",
|
30
|
+
"spec/delayed_method_spec.rb",
|
31
|
+
"spec/job_spec.rb",
|
32
|
+
"spec/story_spec.rb",
|
33
|
+
"tasks/jobs.rake",
|
34
|
+
"tasks/tasks.rb"
|
34
35
|
]
|
35
|
-
s.
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
s.homepage = %q{http://github.com/tobi/delayed_job/tree/master}
|
37
|
+
s.rdoc_options = ["--main", "README.textile", "--inline-source", "--line-numbers"]
|
38
|
+
s.require_paths = ["lib"]
|
39
|
+
s.rubygems_version = %q{1.3.3}
|
40
|
+
s.summary = %q{Database-backed asynchronous priority queue system -- Extracted from Shopify}
|
41
|
+
s.test_files = [
|
42
|
+
"spec/delayed_method_spec.rb",
|
43
|
+
"spec/story_spec.rb",
|
44
|
+
"spec/database.rb",
|
45
|
+
"spec/job_spec.rb"
|
40
46
|
]
|
47
|
+
|
48
|
+
if s.respond_to? :specification_version then
|
49
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
50
|
+
s.specification_version = 3
|
51
|
+
|
52
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
53
|
+
else
|
54
|
+
end
|
55
|
+
else
|
56
|
+
end
|
41
57
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class DelayedJobGenerator < Rails::Generator::Base
|
2
|
+
def manifest
|
3
|
+
record do |m|
|
4
|
+
m.migration_template 'migration.rb', 'db/migrate',
|
5
|
+
:assigns => { :migration_name => 'CreateDelayedJobs' },
|
6
|
+
:migration_file_name => "create_delayed_jobs"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class CreateDelayedJobs < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :delayed_jobs do |t|
|
4
|
+
t.integer :priority, :default => 0
|
5
|
+
t.integer :attempts, :default => 0
|
6
|
+
t.text :handler
|
7
|
+
t.string :job_type
|
8
|
+
t.string :last_error
|
9
|
+
t.datetime :run_at
|
10
|
+
t.datetime :locked_at
|
11
|
+
t.datetime :failed_at
|
12
|
+
t.string :locked_by
|
13
|
+
t.datetime :finished_at
|
14
|
+
t.timestamps
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.down
|
19
|
+
drop_table :delayed_jobs
|
20
|
+
end
|
21
|
+
end
|
data/lib/delayed/job.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
1
3
|
module Delayed
|
2
4
|
|
3
5
|
class DeserializationError < StandardError
|
@@ -16,20 +18,27 @@ module Delayed
|
|
16
18
|
cattr_accessor :destroy_failed_jobs
|
17
19
|
self.destroy_failed_jobs = true
|
18
20
|
|
21
|
+
# By default successful jobs are destroyed after finished.
|
22
|
+
# If you want to keep them around (for statistics/monitoring),
|
23
|
+
# set this to false.
|
24
|
+
cattr_accessor :destroy_successful_jobs
|
25
|
+
self.destroy_successful_jobs = true
|
26
|
+
|
19
27
|
# Every worker has a unique name which by default is the pid of the process.
|
20
28
|
# There are some advantages to overriding this with something which survives worker retarts:
|
21
29
|
# Workers can safely resume working on tasks which are locked by themselves. The worker will assume that it crashed before.
|
22
30
|
cattr_accessor :worker_name
|
23
31
|
self.worker_name = "host:#{Socket.gethostname} pid:#{Process.pid}" rescue "pid:#{Process.pid}"
|
24
32
|
|
25
|
-
NextTaskSQL = '(run_at <= ? AND (locked_at IS NULL OR locked_at < ?) OR (locked_by = ?)) AND failed_at IS NULL'
|
33
|
+
NextTaskSQL = '(run_at <= ? AND (locked_at IS NULL OR locked_at < ?) OR (locked_by = ?)) AND failed_at IS NULL AND finished_at IS NULL'
|
26
34
|
NextTaskOrder = 'priority DESC, run_at ASC'
|
27
35
|
|
28
36
|
ParseObjectFromYaml = /\!ruby\/\w+\:([^\s]+)/
|
29
37
|
|
30
|
-
cattr_accessor :min_priority, :max_priority
|
38
|
+
cattr_accessor :min_priority, :max_priority, :job_types
|
31
39
|
self.min_priority = nil
|
32
40
|
self.max_priority = nil
|
41
|
+
self.job_types = nil
|
33
42
|
|
34
43
|
# When a worker is exiting, make sure we don't have any locked jobs.
|
35
44
|
def self.clear_locks!
|
@@ -57,13 +66,14 @@ module Delayed
|
|
57
66
|
end
|
58
67
|
|
59
68
|
def payload_object=(object)
|
60
|
-
self['
|
69
|
+
self['job_type'] = object.class.to_s
|
70
|
+
self['handler'] = object.to_yaml
|
61
71
|
end
|
62
72
|
|
63
73
|
# Reschedule the job in the future (when a job fails).
|
64
74
|
# Uses an exponential scale depending on the number of failed attempts.
|
65
75
|
def reschedule(message, backtrace = [], time = nil)
|
66
|
-
if self.attempts < MAX_ATTEMPTS
|
76
|
+
if self.attempts < (payload_object.send(:max_attempts) rescue MAX_ATTEMPTS)
|
67
77
|
time ||= Job.db_time_now + (attempts ** 4) + 5
|
68
78
|
|
69
79
|
self.attempts += 1
|
@@ -89,11 +99,11 @@ module Delayed
|
|
89
99
|
|
90
100
|
begin
|
91
101
|
runtime = Benchmark.realtime do
|
92
|
-
|
93
|
-
destroy
|
102
|
+
Timeout.timeout(max_run_time.to_i) { invoke_job }
|
94
103
|
end
|
95
|
-
|
96
|
-
|
104
|
+
destroy_successful_jobs ? destroy :
|
105
|
+
update_attribute(:finished_at, Time.now)
|
106
|
+
Delayed::Worker.logger.info "* [JOB] #{name} completed after %.4f" % runtime
|
97
107
|
return true # did work
|
98
108
|
rescue Exception => e
|
99
109
|
reschedule e.message, e.backtrace
|
@@ -136,6 +146,11 @@ module Delayed
|
|
136
146
|
conditions << max_priority
|
137
147
|
end
|
138
148
|
|
149
|
+
if self.job_types
|
150
|
+
sql << ' AND (job_type IN (?))'
|
151
|
+
conditions << job_types
|
152
|
+
end
|
153
|
+
|
139
154
|
conditions.unshift(sql)
|
140
155
|
|
141
156
|
records = ActiveRecord::Base.silence do
|
data/lib/delayed/worker.rb
CHANGED
@@ -13,6 +13,7 @@ module Delayed
|
|
13
13
|
@quiet = options[:quiet]
|
14
14
|
Delayed::Job.min_priority = options[:min_priority] if options.has_key?(:min_priority)
|
15
15
|
Delayed::Job.max_priority = options[:max_priority] if options.has_key?(:max_priority)
|
16
|
+
Delayed::Job.job_types = options[:job_types] if options.has_key?(:job_types)
|
16
17
|
end
|
17
18
|
|
18
19
|
def start
|
data/spec/database.rb
CHANGED
@@ -3,13 +3,17 @@ $:.unshift(File.dirname(__FILE__) + '/../../rspec/lib')
|
|
3
3
|
|
4
4
|
require 'rubygems'
|
5
5
|
require 'active_record'
|
6
|
-
gem 'sqlite3-ruby'
|
7
6
|
|
8
7
|
require File.dirname(__FILE__) + '/../init'
|
9
8
|
require 'spec'
|
10
|
-
|
11
|
-
|
12
|
-
ActiveRecord::Base.
|
9
|
+
|
10
|
+
logger = Logger.new('/tmp/dj.log')
|
11
|
+
ActiveRecord::Base.logger = logger
|
12
|
+
Delayed::Worker.logger = logger
|
13
|
+
ActiveRecord::Base.establish_connection(
|
14
|
+
:adapter => (PLATFORM =~ /java/ ? 'jdbcsqlite3' : 'sqlite3'),
|
15
|
+
:database => '/tmp/jobs.sqlite'
|
16
|
+
)
|
13
17
|
ActiveRecord::Migration.verbose = false
|
14
18
|
|
15
19
|
ActiveRecord::Schema.define do
|
@@ -18,11 +22,13 @@ ActiveRecord::Schema.define do
|
|
18
22
|
table.integer :priority, :default => 0
|
19
23
|
table.integer :attempts, :default => 0
|
20
24
|
table.text :handler
|
25
|
+
table.string :job_type
|
21
26
|
table.string :last_error
|
22
27
|
table.datetime :run_at
|
23
28
|
table.datetime :locked_at
|
24
29
|
table.string :locked_by
|
25
30
|
table.datetime :failed_at
|
31
|
+
table.datetime :finished_at
|
26
32
|
table.timestamps
|
27
33
|
end
|
28
34
|
|
@@ -35,8 +41,8 @@ end
|
|
35
41
|
|
36
42
|
# Purely useful for test cases...
|
37
43
|
class Story < ActiveRecord::Base
|
38
|
-
def tell; text; end
|
44
|
+
def tell; text; end
|
39
45
|
def whatever(n, _); tell*n; end
|
40
|
-
|
46
|
+
|
41
47
|
handle_asynchronously :whatever
|
42
48
|
end
|
data/spec/job_spec.rb
CHANGED
@@ -5,11 +5,19 @@ class SimpleJob
|
|
5
5
|
def perform; @@runs += 1; end
|
6
6
|
end
|
7
7
|
|
8
|
+
class CustomJob < SimpleJob
|
9
|
+
def max_attempts; 3; end
|
10
|
+
end
|
11
|
+
|
8
12
|
class ErrorJob
|
9
13
|
cattr_accessor :runs; self.runs = 0
|
10
14
|
def perform; raise 'did not work'; end
|
11
15
|
end
|
12
16
|
|
17
|
+
class LongRunningJob
|
18
|
+
def perform; sleep 250; end
|
19
|
+
end
|
20
|
+
|
13
21
|
module M
|
14
22
|
class ModuleJob
|
15
23
|
cattr_accessor :runs; self.runs = 0
|
@@ -69,8 +77,43 @@ describe Delayed::Job do
|
|
69
77
|
|
70
78
|
SimpleJob.runs.should == 1
|
71
79
|
end
|
72
|
-
|
73
|
-
|
80
|
+
|
81
|
+
it "should work on specified job types" do
|
82
|
+
SimpleJob.runs.should == 0
|
83
|
+
|
84
|
+
Delayed::Job.job_types = "SimpleJob"
|
85
|
+
Delayed::Job.enqueue SimpleJob.new
|
86
|
+
Delayed::Job.work_off
|
87
|
+
|
88
|
+
SimpleJob.runs.should == 1
|
89
|
+
|
90
|
+
Delayed::Job.job_types = nil
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should not work on unspecified job types" do
|
94
|
+
SimpleJob.runs.should == 0
|
95
|
+
|
96
|
+
Delayed::Job.job_types = "AnotherJob"
|
97
|
+
Delayed::Job.enqueue SimpleJob.new
|
98
|
+
Delayed::Job.work_off
|
99
|
+
|
100
|
+
SimpleJob.runs.should == 0
|
101
|
+
|
102
|
+
Delayed::Job.job_types = nil
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should work on specified job types even when it's a list" do
|
106
|
+
SimpleJob.runs.should == 0
|
107
|
+
|
108
|
+
Delayed::Job.job_types = %w( Whatever SimpleJob )
|
109
|
+
Delayed::Job.enqueue SimpleJob.new
|
110
|
+
Delayed::Job.work_off
|
111
|
+
|
112
|
+
SimpleJob.runs.should == 1
|
113
|
+
|
114
|
+
Delayed::Job.job_types = nil
|
115
|
+
end
|
116
|
+
|
74
117
|
it "should work with eval jobs" do
|
75
118
|
$eval_job_ran = false
|
76
119
|
|
@@ -92,7 +135,49 @@ describe Delayed::Job do
|
|
92
135
|
|
93
136
|
M::ModuleJob.runs.should == 1
|
94
137
|
end
|
95
|
-
|
138
|
+
|
139
|
+
it "should be destroyed if it succeeded and we want to destroy jobs" do
|
140
|
+
default = Delayed::Job.destroy_successful_jobs
|
141
|
+
Delayed::Job.destroy_successful_jobs = true
|
142
|
+
|
143
|
+
Delayed::Job.enqueue SimpleJob.new
|
144
|
+
Delayed::Job.work_off
|
145
|
+
|
146
|
+
Delayed::Job.count.should == 0
|
147
|
+
|
148
|
+
Delayed::Job.destroy_successful_jobs = default
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should be kept if it succeeded and we don't want to destroy jobs" do
|
152
|
+
default = Delayed::Job.destroy_successful_jobs
|
153
|
+
Delayed::Job.destroy_successful_jobs = false
|
154
|
+
|
155
|
+
Delayed::Job.enqueue SimpleJob.new
|
156
|
+
Delayed::Job.work_off
|
157
|
+
|
158
|
+
Delayed::Job.count.should == 1
|
159
|
+
|
160
|
+
Delayed::Job.destroy_successful_jobs = default
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should be finished if it succeeded and we don't want to destroy jobs" do
|
164
|
+
default = Delayed::Job.destroy_successful_jobs
|
165
|
+
Delayed::Job.destroy_successful_jobs = false
|
166
|
+
@job = Delayed::Job.create :payload_object => SimpleJob.new
|
167
|
+
|
168
|
+
@job.reload.finished_at.should == nil
|
169
|
+
Delayed::Job.work_off
|
170
|
+
@job.reload.finished_at.should_not == nil
|
171
|
+
|
172
|
+
Delayed::Job.destroy_successful_jobs = default
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should never find finished jobs" do
|
176
|
+
@job = Delayed::Job.create :payload_object => SimpleJob.new,
|
177
|
+
:finished_at => Time.now
|
178
|
+
Delayed::Job.find_available(1).length.should == 0
|
179
|
+
end
|
180
|
+
|
96
181
|
it "should re-schedule by about 1 second at first and increment this more and more minutes when it fails to execute properly" do
|
97
182
|
Delayed::Job.enqueue ErrorJob.new
|
98
183
|
Delayed::Job.work_off(1)
|
@@ -100,7 +185,7 @@ describe Delayed::Job do
|
|
100
185
|
job = Delayed::Job.find(:first)
|
101
186
|
|
102
187
|
job.last_error.should =~ /did not work/
|
103
|
-
job.last_error.should =~ /job_spec.rb:
|
188
|
+
job.last_error.should =~ /job_spec.rb:14:in `perform'/
|
104
189
|
job.attempts.should == 1
|
105
190
|
|
106
191
|
job.run_at.should > Delayed::Job.db_time_now - 10.minutes
|
@@ -171,6 +256,24 @@ describe Delayed::Job do
|
|
171
256
|
Delayed::Job.destroy_failed_jobs = default
|
172
257
|
end
|
173
258
|
|
259
|
+
it "should allow jobs to override max_attemps and behave appropriately" do
|
260
|
+
default = Delayed::Job.destroy_failed_jobs
|
261
|
+
Delayed::Job.destroy_failed_jobs = true
|
262
|
+
|
263
|
+
@job = Delayed::Job.create :payload_object => CustomJob.new, :attempts => 5
|
264
|
+
@job.should_receive(:destroy)
|
265
|
+
@job.reschedule 'FAIL'
|
266
|
+
|
267
|
+
Delayed::Job.destroy_failed_jobs = default
|
268
|
+
end
|
269
|
+
|
270
|
+
it "should fail after MAX_RUN_TIME" do
|
271
|
+
@job = Delayed::Job.create :payload_object => LongRunningJob.new
|
272
|
+
Delayed::Job.reserve_and_run_one_job(1.second)
|
273
|
+
@job.reload.last_error.should =~ /expired/
|
274
|
+
@job.attempts.should == 1
|
275
|
+
end
|
276
|
+
|
174
277
|
it "should never find failed jobs" do
|
175
278
|
@job = Delayed::Job.create :payload_object => SimpleJob.new, :attempts => 50, :failed_at => Time.now
|
176
279
|
Delayed::Job.find_available(1).length.should == 0
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blaxter-delayed_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.7.
|
4
|
+
version: 1.7.99
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- "Tobias L\xC3\xBCtke"
|
@@ -9,11 +9,11 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2009-08-06 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
16
|
-
description:
|
16
|
+
description: 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.
|
17
17
|
email: tobi@leetsoft.com
|
18
18
|
executables: []
|
19
19
|
|
@@ -22,15 +22,23 @@ extensions: []
|
|
22
22
|
extra_rdoc_files:
|
23
23
|
- README.textile
|
24
24
|
files:
|
25
|
+
- .gitignore
|
25
26
|
- MIT-LICENSE
|
26
27
|
- README.textile
|
28
|
+
- Rakefile
|
27
29
|
- delayed_job.gemspec
|
30
|
+
- generators/delayed_job/delayed_job_generator.rb
|
31
|
+
- generators/delayed_job/templates/migration.rb
|
28
32
|
- init.rb
|
29
33
|
- lib/delayed/job.rb
|
30
34
|
- lib/delayed/message_sending.rb
|
31
35
|
- lib/delayed/performable_method.rb
|
32
36
|
- lib/delayed/worker.rb
|
33
37
|
- lib/delayed_job.rb
|
38
|
+
- spec/database.rb
|
39
|
+
- spec/delayed_method_spec.rb
|
40
|
+
- spec/job_spec.rb
|
41
|
+
- spec/story_spec.rb
|
34
42
|
- tasks/jobs.rake
|
35
43
|
- tasks/tasks.rb
|
36
44
|
has_rdoc: false
|
@@ -40,6 +48,8 @@ post_install_message:
|
|
40
48
|
rdoc_options:
|
41
49
|
- --main
|
42
50
|
- README.textile
|
51
|
+
- --inline-source
|
52
|
+
- --line-numbers
|
43
53
|
require_paths:
|
44
54
|
- lib
|
45
55
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -59,10 +69,10 @@ requirements: []
|
|
59
69
|
rubyforge_project:
|
60
70
|
rubygems_version: 1.3.5
|
61
71
|
signing_key:
|
62
|
-
specification_version:
|
72
|
+
specification_version: 3
|
63
73
|
summary: Database-backed asynchronous priority queue system -- Extracted from Shopify
|
64
74
|
test_files:
|
65
|
-
- spec/database.rb
|
66
75
|
- spec/delayed_method_spec.rb
|
67
|
-
- spec/job_spec.rb
|
68
76
|
- spec/story_spec.rb
|
77
|
+
- spec/database.rb
|
78
|
+
- spec/job_spec.rb
|