delayed_job 4.0.0.beta1 → 4.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MTQxN2VkZDVhM2YxY2RhNzE0NzRiN2ZlMDE0ZmQ3Y2I3ZjM5ODkyMA==
5
+ data.tar.gz: !binary |-
6
+ NjI3N2EyYTkzY2E2M2YyNmE2OWYxNTVhOWZkNWI5ZGViZWEzYjIzNQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MjhjOGJkN2YxNGM0NzI0ZDIxOTE0ZGVjNTk0OGZhZWQxZmY4YTkxYTczNWRk
10
+ ZWIxNTEzYWE4Mzc4OGE0Mjc2NzhhMDQ0NjdiZGVjMDc1YzEyMDUyOTFmYmRl
11
+ OTdmNzYyZDQ4YzJjZWVlOGUzNjczY2VlOTczYTc5Mjk1ZjhjZTE=
12
+ data.tar.gz: !binary |-
13
+ MDcxN2FkZDMxYWVhOTI2NjBmN2FmNzgyN2I0MTZkMmM3YmNjZWM4NTk2MzVl
14
+ ZDA2ZWQ1ZWE4YTE3OGI0ZDlmMTNjMGQ0ZDgyNTllNWM1OTczNzQxNWY3ZTFk
15
+ NjFkM2ZjMGEzOGYyMTQzNjNhMjg5ODRjNGEzNTExNGM2YjJiZGE=
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ 4.0.0.beta2 - 2013-05-28
2
+ ========================
3
+ * Rails 4 compatibility
4
+ * Threaded startup script for faster multi-worker startup
5
+ * YAML compatibility changes
6
+ * Added jobs:check rake task
7
+
1
8
  4.0.0.beta1 - 2013-03-02
2
9
  ========================
3
10
  * Rails 4 compatibility
data/README.md CHANGED
@@ -40,11 +40,15 @@ for other backends](http://wiki.github.com/collectiveidea/delayed_job/backends).
40
40
 
41
41
  If you plan to use delayed_job with Active Record, add `delayed_job_active_record` to your `Gemfile`.
42
42
 
43
- gem 'delayed_job_active_record'
43
+ ```ruby
44
+ gem 'delayed_job_active_record'
45
+ ```
44
46
 
45
47
  If you plan to use delayed_job with Mongoid, add `delayed_job_mongoid` to your `Gemfile`.
46
48
 
47
- gem 'delayed_job_mongoid'
49
+ ```ruby
50
+ gem 'delayed_job_mongoid'
51
+ ```
48
52
 
49
53
  Run `bundle install` to install the backend and delayed_job gems.
50
54
 
@@ -67,11 +71,13 @@ Queuing Jobs
67
71
  ============
68
72
  Call `.delay.method(params)` on any object and it will be processed in the background.
69
73
 
70
- # without delayed_job
71
- @user.activate!(@device)
74
+ ```ruby
75
+ # without delayed_job
76
+ @user.activate!(@device)
72
77
 
73
- # with delayed_job
74
- @user.delay.activate!(@device)
78
+ # with delayed_job
79
+ @user.delay.activate!(@device)
80
+ ```
75
81
 
76
82
  If a method should always be run in the background, you can call
77
83
  `#handle_asynchronously` after the method declaration:
@@ -123,6 +129,8 @@ class LongTasks
123
129
  end
124
130
  ```
125
131
 
132
+ If you ever want to call a `handle_asynchronously`'d method without Delayed Job, for instance while debugging something at the console, just add `_without_delay` to the method name. For instance, if your original method was `foo`, then call `foo_without_delay`.
133
+
126
134
  Rails 3 Mailers
127
135
  ===============
128
136
  Due to how mailers are implemented in Rails 3, we had to do a little work around to get delayed_job to work.
@@ -180,6 +188,8 @@ You can then do the following:
180
188
  # or to run in the foreground
181
189
  RAILS_ENV=production script/delayed_job run --exit-on-complete
182
190
 
191
+ **Rails 4:** *replace script/delayed_job with bin/delayed_job*
192
+
183
193
  Workers can be running on any computer, as long as they have access to the
184
194
  database and their clock is in sync. Keep in mind that each worker will check
185
195
  the database at least every 5 seconds.
@@ -193,6 +203,21 @@ Work off queues by setting the `QUEUE` or `QUEUES` environment variable.
193
203
 
194
204
  QUEUE=tracking rake jobs:work
195
205
  QUEUES=mailers,tasks rake jobs:work
206
+
207
+ Restarting delayed_job
208
+ ======================
209
+
210
+ The following syntax will restart delayed jobs:
211
+
212
+ RAILS_ENV=production script/delayed_job restart
213
+
214
+ To restart multiple delayed_job workers:
215
+
216
+ RAILS_ENV=production script/delayed_job -n2 restart
217
+
218
+ **Rails 4:** *replace script/delayed_job with bin/delayed_job*
219
+
220
+
196
221
 
197
222
  Custom Jobs
198
223
  ===========
@@ -207,6 +232,19 @@ end
207
232
 
208
233
  Delayed::Job.enqueue NewsletterJob.new('lorem ipsum...', Customers.find(:all).collect(&:email))
209
234
  ```
235
+ To set a per-job max attempts that overrides the Delayed::Worker.max_attempts you can define a max_attempts method on the job
236
+ ```ruby
237
+ class NewsletterJob < Struct.new(:text, :emails)
238
+ def perform
239
+ emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
240
+ end
241
+
242
+ def max_attempts
243
+ return 3
244
+ end
245
+ end
246
+ ````
247
+
210
248
 
211
249
  Hooks
212
250
  =====
@@ -0,0 +1,14 @@
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/bin/delayed_job start"
14
+ stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job stop"
@@ -0,0 +1,34 @@
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
+ # you might also need to change the program strings to
7
+ # "/bin/su - {username} -c '/usr/bin/env ...'"
8
+ # to load your shell environment.
9
+ #
10
+ # 3. add this to your /etc/monit/monitrc
11
+ #
12
+ # include /var/www/apps/{app_name}/shared/delayed_job.monitrc
13
+ #
14
+ # The processes are grouped so that monit can act on them as a whole, e.g.
15
+ #
16
+ # monit -g delayed_job restart
17
+
18
+ check process delayed_job_0
19
+ with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.0.pid
20
+ start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job start -i 0"
21
+ stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job stop -i 0"
22
+ group delayed_job
23
+
24
+ check process delayed_job_1
25
+ with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.1.pid
26
+ start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job start -i 1"
27
+ stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job stop -i 1"
28
+ group delayed_job
29
+
30
+ check process delayed_job_2
31
+ with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.2.pid
32
+ start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job start -i 2"
33
+ stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job stop -i 2"
34
+ group delayed_job
data/delayed_job.gemspec CHANGED
@@ -13,5 +13,5 @@ Gem::Specification.new do |spec|
13
13
  spec.require_paths = ['lib']
14
14
  spec.summary = 'Database-backed asynchronous priority queue system -- Extracted from Shopify'
15
15
  spec.test_files = Dir.glob('spec/**/*')
16
- spec.version = '4.0.0.beta1'
16
+ spec.version = '4.0.0.beta2'
17
17
  end
@@ -78,10 +78,14 @@ module Delayed
78
78
  process_name = "delayed_job.#{@options[:identifier]}"
79
79
  run_process(process_name, dir)
80
80
  else
81
+ threads = []
81
82
  worker_count.times do |worker_index|
82
- process_name = worker_count == 1 ? "delayed_job" : "delayed_job.#{worker_index}"
83
- run_process(process_name, dir)
83
+ threads << Thread.start do
84
+ process_name = worker_count == 1 ? "delayed_job" : "delayed_job.#{worker_index}"
85
+ run_process(process_name, dir)
86
+ end
84
87
  end
88
+ threads.each(&:join)
85
89
  end
86
90
  end
87
91
 
@@ -0,0 +1,27 @@
1
+ require 'active_support/version'
2
+
3
+ module Delayed
4
+ module Compatibility
5
+ if ActiveSupport::VERSION::MAJOR >= 4
6
+ require 'active_support/proxy_object'
7
+
8
+ def self.executable_prefix
9
+ 'bin'
10
+ end
11
+
12
+ def self.proxy_object_class
13
+ ActiveSupport::ProxyObject
14
+ end
15
+ else
16
+ require 'active_support/basic_object'
17
+
18
+ def self.executable_prefix
19
+ 'script'
20
+ end
21
+
22
+ def self.proxy_object_class
23
+ ActiveSupport::BasicObject
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,19 +1,7 @@
1
- if ActiveSupport::VERSION::MAJOR == 4
2
- require 'active_support/proxy_object'
3
- else
4
- require 'active_support/basic_object'
5
- end
6
-
7
1
  require 'active_support/core_ext/module/aliasing'
8
2
 
9
3
  module Delayed
10
- if ActiveSupport::VERSION::MAJOR == 4
11
- klass = ActiveSupport::ProxyObject
12
- else
13
- klass = ActiveSupport::BasicObject
14
- end
15
-
16
- class DelayProxy < klass
4
+ class DelayProxy < Delayed::Compatibility.proxy_object_class
17
5
  def initialize(payload_class, target, options)
18
6
  @payload_class = payload_class
19
7
  @target = target
@@ -1,11 +1,18 @@
1
1
  if defined?(ActiveRecord)
2
2
  ActiveRecord::Base.class_eval do
3
- def encode_with_override(coder)
4
- encode_with_without_override(coder)
5
- coder.tag = "!ruby/ActiveRecord:#{self.class.name}"
3
+ if instance_methods.include?(:encode_with)
4
+ def encode_with_override(coder)
5
+ encode_with_without_override(coder)
6
+ coder.tag = "!ruby/ActiveRecord:#{self.class.name}"
7
+ end
8
+ alias_method :encode_with_without_override, :encode_with
9
+ alias_method :encode_with, :encode_with_override
10
+ else
11
+ def encode_with(coder)
12
+ coder["attributes"] = attributes
13
+ coder.tag = "!ruby/ActiveRecord:#{self.class.name}"
14
+ end
6
15
  end
7
- alias_method :encode_with_without_override, :encode_with
8
- alias_method :encode_with, :encode_with_override
9
16
  end
10
17
  end
11
18
 
data/lib/delayed/tasks.rb CHANGED
@@ -22,4 +22,17 @@ namespace :jobs do
22
22
  :quiet => false
23
23
  }
24
24
  end
25
+
26
+ desc "Exit with error status if any jobs older than max_age seconds haven't been attempted yet."
27
+ task :check, [:max_age] => :environment do |_, args|
28
+ args.with_defaults(:max_age => 300)
29
+
30
+ unprocessed_jobs = Delayed::Job.where('attempts = 0 AND created_at < ?', Time.now - args[:max_age].to_i).count
31
+
32
+ if unprocessed_jobs > 0
33
+ fail "#{unprocessed_jobs} jobs older than #{args[:max_age]} seconds have not been processed yet"
34
+ end
35
+
36
+ end
37
+
25
38
  end
@@ -8,6 +8,7 @@ require 'benchmark'
8
8
 
9
9
  module Delayed
10
10
  class Worker
11
+ DEFAULT_LOG_LEVEL = Logger::INFO
11
12
  DEFAULT_SLEEP_DELAY = 5
12
13
  DEFAULT_MAX_ATTEMPTS = 25
13
14
  DEFAULT_MAX_RUN_TIME = 4.hours
@@ -115,7 +116,7 @@ module Delayed
115
116
  end
116
117
 
117
118
  # Every worker has a unique name which by default is the pid of the process. There are some
118
- # advantages to overriding this with something which survives worker retarts: Workers can#
119
+ # advantages to overriding this with something which survives worker restarts: Workers can
119
120
  # safely resume working on tasks which are locked by themselves. The worker will assume that
120
121
  # it crashed before.
121
122
  def name
@@ -162,7 +163,7 @@ module Delayed
162
163
  sleep(self.class.sleep_delay) unless stop?
163
164
  end
164
165
  else
165
- say "#{count} jobs processed at %.4f j/s, %d failed ..." % [count / @realtime, @result.last]
166
+ say "#{count} jobs processed at %.4f j/s, %d failed" % [count / @realtime, @result.last]
166
167
  end
167
168
 
168
169
  break if stop?
@@ -199,11 +200,12 @@ module Delayed
199
200
  end
200
201
 
201
202
  def run(job)
203
+ job_say job, 'RUNNING'
202
204
  runtime = Benchmark.realtime do
203
205
  Timeout.timeout(self.class.max_run_time.to_i, WorkerTimeout) { job.invoke_job }
204
206
  job.destroy
205
207
  end
206
- say "#{job.name} completed after %.4f" % runtime
208
+ job_say job, 'COMPLETED after %.4f' % runtime
207
209
  return true # did work
208
210
  rescue DeserializationError => error
209
211
  job.last_error = "#{error.message}\n#{error.backtrace.join("\n")}"
@@ -222,7 +224,7 @@ module Delayed
222
224
  job.unlock
223
225
  job.save!
224
226
  else
225
- say "PERMANENTLY removing #{job.name} because of #{job.attempts} consecutive failures.", Logger::INFO
227
+ job_say job, "REMOVED permanently because of #{job.attempts} consecutive failures", Logger::ERROR
226
228
  failed(job)
227
229
  end
228
230
  end
@@ -234,7 +236,12 @@ module Delayed
234
236
  end
235
237
  end
236
238
 
237
- def say(text, level = Logger::INFO)
239
+ def job_say(job, text, level = DEFAULT_LOG_LEVEL)
240
+ text = "Job #{job.name} (id=#{job.id}) #{text}"
241
+ say text, level
242
+ end
243
+
244
+ def say(text, level = DEFAULT_LOG_LEVEL)
238
245
  text = "[Worker(#{name})] #{text}"
239
246
  puts text unless @quiet
240
247
  logger.add level, "#{Time.now.strftime('%FT%T%z')}: #{text}" if logger
@@ -248,7 +255,7 @@ module Delayed
248
255
 
249
256
  def handle_failed_job(job, error)
250
257
  job.last_error = "#{error.message}\n#{error.backtrace.join("\n")}"
251
- say "#{job.name} failed with #{error.class.name}: #{error.message} - #{job.attempts} failed attempts", Logger::ERROR
258
+ job_say job, "FAILED (#{job.attempts} prior attempts) with #{error.class.name}: #{error.message}", Logger::ERROR
252
259
  reschedule(job)
253
260
  end
254
261
 
@@ -256,7 +263,7 @@ module Delayed
256
263
  # If no jobs are left we return nil
257
264
  def reserve_and_run_one_job
258
265
  job = Delayed::Job.reserve(self)
259
- self.class.lifecycle.run_callbacks(:perform, self, job){ result = run(job) } if job
266
+ self.class.lifecycle.run_callbacks(:perform, self, job){ run(job) } if job
260
267
  end
261
268
  end
262
269
 
data/lib/delayed_job.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'active_support'
2
+ require 'delayed/compatibility'
2
3
  require 'delayed/exceptions'
3
4
  require 'delayed/message_sending'
4
5
  require 'delayed/performable_method'
@@ -1,11 +1,12 @@
1
1
  require 'rails/generators'
2
+ require 'delayed/compatibility'
2
3
 
3
4
  class DelayedJobGenerator < Rails::Generators::Base
4
5
 
5
6
  self.source_paths << File.join(File.dirname(__FILE__), 'templates')
6
7
 
7
- def create_script_file
8
- template 'script', 'script/delayed_job'
9
- chmod 'script/delayed_job', 0755
8
+ def create_executable_file
9
+ template "script", "#{Delayed::Compatibility.executable_prefix}/delayed_job"
10
+ chmod "#{Delayed::Compatibility.executable_prefix}/delayed_job", 0755
10
11
  end
11
12
  end
data/spec/helper.rb CHANGED
@@ -1,12 +1,3 @@
1
- require 'simplecov'
2
- require 'coveralls'
3
-
4
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
5
- SimpleCov::Formatter::HTMLFormatter,
6
- Coveralls::SimpleCov::Formatter
7
- ]
8
- SimpleCov.start
9
-
10
1
  require 'logger'
11
2
  require 'rspec'
12
3
 
@@ -17,6 +8,15 @@ require 'active_record'
17
8
  require 'delayed_job'
18
9
  require 'delayed/backend/shared_spec'
19
10
 
11
+ require 'simplecov'
12
+ require 'coveralls'
13
+
14
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
15
+ SimpleCov::Formatter::HTMLFormatter,
16
+ Coveralls::SimpleCov::Formatter
17
+ ]
18
+ SimpleCov.start
19
+
20
20
  Delayed::Worker.logger = Logger.new('/tmp/dj.log')
21
21
  ENV['RAILS_ENV'] = 'test'
22
22
 
data/spec/worker_spec.rb CHANGED
@@ -21,6 +21,19 @@ describe Delayed::Worker do
21
21
  end
22
22
  end
23
23
 
24
+ describe "job_say" do
25
+ before do
26
+ @worker = Delayed::Worker.new
27
+ @job = stub('job', :id => 123, :name => 'ExampleJob')
28
+ end
29
+
30
+ it "logs with job name and id" do
31
+ @worker.should_receive(:say).
32
+ with('Job ExampleJob (id=123) message', Delayed::Worker::DEFAULT_LOG_LEVEL)
33
+ @worker.job_say(@job, 'message')
34
+ end
35
+ end
36
+
24
37
  context "worker read-ahead" do
25
38
  before do
26
39
  @read_ahead = Delayed::Worker.read_ahead
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: delayed_job
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0.beta1
5
- prerelease: 6
4
+ version: 4.0.0.beta2
6
5
  platform: ruby
7
6
  authors:
8
7
  - Brandon Keepers
@@ -16,12 +15,12 @@ authors:
16
15
  autorequire:
17
16
  bindir: bin
18
17
  cert_chain: []
19
- date: 2013-03-02 00:00:00.000000000 Z
18
+ date: 2013-05-28 00:00:00.000000000 Z
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
21
+ prerelease: false
22
22
  name: activesupport
23
- requirement: !ruby/object:Gem::Requirement
24
- none: false
23
+ version_requirements: !ruby/object:Gem::Requirement
25
24
  requirements:
26
25
  - - ! '>='
27
26
  - !ruby/object:Gem::Version
@@ -29,10 +28,7 @@ dependencies:
29
28
  - - <
30
29
  - !ruby/object:Gem::Version
31
30
  version: '4.1'
32
- type: :runtime
33
- prerelease: false
34
- version_requirements: !ruby/object:Gem::Requirement
35
- none: false
31
+ requirement: !ruby/object:Gem::Requirement
36
32
  requirements:
37
33
  - - ! '>='
38
34
  - !ruby/object:Gem::Version
@@ -40,6 +36,7 @@ dependencies:
40
36
  - - <
41
37
  - !ruby/object:Gem::Version
42
38
  version: '4.1'
39
+ type: :runtime
43
40
  description: Delayed_job (or DJ) encapsulates the common pattern of asynchronously
44
41
  executing longer tasks in the background. It is a direct extraction from Shopify
45
42
  where the job table is responsible for a multitude of core tasks.
@@ -57,9 +54,12 @@ files:
57
54
  - delayed_job.gemspec
58
55
  - contrib/delayed_job.monitrc
59
56
  - contrib/delayed_job_multiple.monitrc
57
+ - contrib/delayed_job_rails_4.monitrc
58
+ - contrib/delayed_job_rails_4_multiple.monitrc
60
59
  - lib/delayed/backend/base.rb
61
60
  - lib/delayed/backend/shared_spec.rb
62
61
  - lib/delayed/command.rb
62
+ - lib/delayed/compatibility.rb
63
63
  - lib/delayed/deserialization_error.rb
64
64
  - lib/delayed/exceptions.rb
65
65
  - lib/delayed/lifecycle.rb
@@ -98,30 +98,26 @@ files:
98
98
  homepage: http://github.com/collectiveidea/delayed_job
99
99
  licenses:
100
100
  - MIT
101
+ metadata: {}
101
102
  post_install_message:
102
103
  rdoc_options: []
103
104
  require_paths:
104
105
  - lib
105
106
  required_ruby_version: !ruby/object:Gem::Requirement
106
- none: false
107
107
  requirements:
108
108
  - - ! '>='
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
- segments:
112
- - 0
113
- hash: -646849104774513769
114
111
  required_rubygems_version: !ruby/object:Gem::Requirement
115
- none: false
116
112
  requirements:
117
113
  - - ! '>'
118
114
  - !ruby/object:Gem::Version
119
115
  version: 1.3.1
120
116
  requirements: []
121
117
  rubyforge_project:
122
- rubygems_version: 1.8.25
118
+ rubygems_version: 2.0.3
123
119
  signing_key:
124
- specification_version: 3
120
+ specification_version: 4
125
121
  summary: Database-backed asynchronous priority queue system -- Extracted from Shopify
126
122
  test_files:
127
123
  - spec/autoloaded/clazz.rb