que 0.11.6 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1d29adfcb566361924bbd594ba6e5c7b93006060
4
- data.tar.gz: 65905d420760dcafc00411f895d6140262d9beb1
3
+ metadata.gz: dd0f74eb9d94a2f949174b802fdd70f898cfe2e3
4
+ data.tar.gz: 60c5ea6a16dd1080bc16fcea19250c01e94937f6
5
5
  SHA512:
6
- metadata.gz: 729dcc64222ed6c9c1d528e72ad4195a9b66aa2275372143f0add9ad051977e967dad6b5dbced2dec86a714553a2994fe2e12e4a2f03224751e2d9670f97d05b
7
- data.tar.gz: 1ffb694bd34229a657e203256e85556b9e84bc293681e71cbb80d98cbf21003651d0d9b80d67f34f0849f2f863743a8d08a5bb24a24e7ae6ed7ec7eaa3f149fb
6
+ metadata.gz: fdc55d830dccfb0eaed7b7220b2bb65a239b0abda3b7cbd749cd87f7835fc6e521db375ef749e0a3f216f82620b6e76fc6dd6edf3b44c5d80f35feb8a43324ea
7
+ data.tar.gz: cecc9a9a728cf2445ffc4fed02c050972e8903cd4639594b069252e10f130c8dc96ea7bf2305c81f7a2b3e045e416d13198c3009cc08074d4545ebd232ef1f18
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ### 0.12.0 (2016-09-09)
2
+
3
+ * The error_handler configuration option has been renamed to error_notifier, which is more descriptive of what it's actually supposed to do. You can still use error_handler for configuration, but you'll get a warning.
4
+
5
+ * Introduced a new framework for handling errors on a per-job basis. See the docs for more information. (#106, #147)
6
+
1
7
  ### 0.11.6 (2016-07-01)
2
8
 
3
9
  * Fix for operating in nested transactions in Rails 5.0. (#160) (greysteil)
@@ -62,7 +68,7 @@
62
68
 
63
69
  ### 0.9.0 (2014-12-16)
64
70
 
65
- * The error_handler callable is now be passed two objects, the error and the job that raised it. If your current error_handler is a proc, as recommended in the docs, you shouldn't need to make any code changes, unless you want to use the job in your error handling. If your error_handler is a lambda, or another callable with a strict arity requirement, you'll want to change it before upgrading. (#69) (statianzo)
71
+ * The error_handler callable is now passed two objects, the error and the job that raised it. If your current error_handler is a proc, as recommended in the docs, you shouldn't need to make any code changes, unless you want to use the job in your error handling. If your error_handler is a lambda, or another callable with a strict arity requirement, you'll want to change it before upgrading. (#69) (statianzo)
66
72
 
67
73
  ### 0.8.2 (2014-10-12)
68
74
 
@@ -20,10 +20,10 @@ end
20
20
 
21
21
  Unlike DelayedJob, however, there is currently no maximum number of failures after which jobs will be deleted. Que's assumption is that if a job is erroring perpetually (and not just transiently), you will want to take action to get the job working properly rather than simply losing it silently.
22
22
 
23
- If you're using an error notification system (highly recommended, of course), you can hook Que into it by setting a callable as the error handler:
23
+ If you're using an error notification system (highly recommended, of course), you can hook Que into it by setting a callable as the error notifier:
24
24
 
25
25
  ```ruby
26
- Que.error_handler = proc do |error, job|
26
+ Que.error_notifier = proc do |error, job|
27
27
  # Do whatever you want with the error object or job row here.
28
28
 
29
29
  # Note that the job passed is not the actual job object, but the hash
data/lib/que.rb CHANGED
@@ -48,7 +48,7 @@ module Que
48
48
  end
49
49
 
50
50
  class << self
51
- attr_accessor :error_handler
51
+ attr_accessor :error_notifier
52
52
  attr_writer :logger, :adapter, :log_formatter, :disable_prepared_statements, :json_converter
53
53
 
54
54
  def connection=(connection)
@@ -131,6 +131,16 @@ module Que
131
131
  @disable_prepared_statements || false
132
132
  end
133
133
 
134
+ def error_handler
135
+ warn "Que.error_handler has been renamed to Que.error_notifier, please update your code. This shim will be removed in Que version 1.0.0."
136
+ error_notifier
137
+ end
138
+
139
+ def error_handler=(p)
140
+ warn "Que.error_handler= has been renamed to Que.error_notifier=, please update your code. This shim will be removed in Que version 1.0.0."
141
+ self.error_notifier = p
142
+ end
143
+
134
144
  def constantize(camel_cased_word)
135
145
  if camel_cased_word.respond_to?(:constantize)
136
146
  # Use ActiveSupport's version if it exists.
data/lib/que/job.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Que
4
4
  class Job
5
- attr_reader :attrs
5
+ attr_reader :attrs, :_error
6
6
 
7
7
  def initialize(attrs)
8
8
  @attrs = attrs
@@ -16,10 +16,35 @@ module Que
16
16
  def _run
17
17
  run(*attrs[:args])
18
18
  destroy unless @destroyed
19
+ rescue => error
20
+ @_error = error
21
+ run_error_notifier = handle_error(error)
22
+ destroy unless @retried || @destroyed
23
+
24
+ if run_error_notifier && Que.error_notifier
25
+ # Protect the work loop from a failure of the error notifier.
26
+ Que.error_notifier.call(error, @attrs) rescue nil
27
+ end
19
28
  end
20
29
 
21
30
  private
22
31
 
32
+ def error_count
33
+ @attrs[:error_count]
34
+ end
35
+
36
+ def handle_error(error)
37
+ error_count = @attrs[:error_count] += 1
38
+ retry_interval = self.class.retry_interval || Job.retry_interval
39
+ wait = retry_interval.respond_to?(:call) ? retry_interval.call(error_count) : retry_interval
40
+ retry_in(wait)
41
+ end
42
+
43
+ def retry_in(period)
44
+ Que.execute :set_error, [period, @_error.message] + @attrs.values_at(:queue, :priority, :run_at, :job_id)
45
+ @retried = true
46
+ end
47
+
23
48
  def destroy
24
49
  Que.execute :destroy_job, attrs.values_at(:queue, :priority, :run_at, :job_id)
25
50
  @destroyed = true
@@ -99,8 +124,13 @@ module Que
99
124
  {:event => :job_race_condition}
100
125
  else
101
126
  klass = class_for(job[:job_class])
102
- klass.new(job)._run
103
- {:event => :job_worked, :job => job}
127
+ instance = klass.new(job)
128
+ instance._run
129
+ if e = instance._error
130
+ {:event => :job_errored, :job => job, :error => e}
131
+ else
132
+ {:event => :job_worked, :job => job}
133
+ end
104
134
  end
105
135
  else
106
136
  {:event => :job_unavailable}
@@ -112,16 +142,16 @@ module Que
112
142
  interval = klass && klass.respond_to?(:retry_interval) && klass.retry_interval || retry_interval
113
143
  delay = interval.respond_to?(:call) ? interval.call(count) : interval
114
144
  message = "#{error.message}\n#{error.backtrace.join("\n")}"
115
- Que.execute :set_error, [count, delay, message] + job.values_at(:queue, :priority, :run_at, :job_id)
145
+ Que.execute :set_error, [delay, message] + job.values_at(:queue, :priority, :run_at, :job_id)
116
146
  end
117
147
  rescue
118
148
  # If we can't reach the database for some reason, too bad, but
119
149
  # don't let it crash the work loop.
120
150
  end
121
151
 
122
- if Que.error_handler
123
- # Similarly, protect the work loop from a failure of the error handler.
124
- Que.error_handler.call(error, job) rescue nil
152
+ if Que.error_notifier
153
+ # Similarly, protect the work loop from a failure of the error notifier.
154
+ Que.error_notifier.call(error, job) rescue nil
125
155
  end
126
156
 
127
157
  return {:event => :job_errored, :error => error, :job => job}
data/lib/que/sql.rb CHANGED
@@ -88,13 +88,13 @@ module Que
88
88
 
89
89
  :set_error => %{
90
90
  UPDATE que_jobs
91
- SET error_count = $1::integer,
92
- run_at = now() + $2::bigint * '1 second'::interval,
93
- last_error = $3::text
94
- WHERE queue = $4::text
95
- AND priority = $5::smallint
96
- AND run_at = $6::timestamptz
97
- AND job_id = $7::bigint
91
+ SET error_count = error_count + 1,
92
+ run_at = now() + $1::bigint * '1 second'::interval,
93
+ last_error = $2::text
94
+ WHERE queue = $3::text
95
+ AND priority = $4::smallint
96
+ AND run_at = $5::timestamptz
97
+ AND job_id = $6::bigint
98
98
  }.freeze,
99
99
 
100
100
  :insert_job => %{
data/lib/que/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Que
4
- Version = '0.11.6'
4
+ Version = '0.12.0'
5
5
  end
@@ -237,7 +237,7 @@ describe Que::Job, '.work' do
237
237
  DB[:que_jobs].count.should be 1
238
238
  job = DB[:que_jobs].first
239
239
  job[:error_count].should be 1
240
- job[:last_error].should =~ /\AErrorJob!\n/
240
+ job[:last_error].should =~ /\AErrorJob!/
241
241
  job[:run_at].should be_within(3).of Time.now + 4
242
242
 
243
243
  DB[:que_jobs].update :error_count => 5,
@@ -251,7 +251,7 @@ describe Que::Job, '.work' do
251
251
  DB[:que_jobs].count.should be 1
252
252
  job = DB[:que_jobs].first
253
253
  job[:error_count].should be 6
254
- job[:last_error].should =~ /\AErrorJob!\n/
254
+ job[:last_error].should =~ /\AErrorJob!/
255
255
  job[:run_at].should be_within(3).of Time.now + 1299
256
256
  end
257
257
 
@@ -270,7 +270,7 @@ describe Que::Job, '.work' do
270
270
  DB[:que_jobs].count.should be 1
271
271
  job = DB[:que_jobs].first
272
272
  job[:error_count].should be 1
273
- job[:last_error].should =~ /\AErrorJob!\n/
273
+ job[:last_error].should =~ /\AErrorJob!/
274
274
  job[:run_at].to_f.should be_within(3).of Time.now.to_f + RetryIntervalJob.retry_interval
275
275
 
276
276
  DB[:que_jobs].update :error_count => 5,
@@ -284,7 +284,7 @@ describe Que::Job, '.work' do
284
284
  DB[:que_jobs].count.should be 1
285
285
  job = DB[:que_jobs].first
286
286
  job[:error_count].should be 6
287
- job[:last_error].should =~ /\AErrorJob!\n/
287
+ job[:last_error].should =~ /\AErrorJob!/
288
288
  job[:run_at].to_f.should be_within(3).of Time.now.to_f + RetryIntervalJob.retry_interval
289
289
  end
290
290
 
@@ -303,7 +303,7 @@ describe Que::Job, '.work' do
303
303
  DB[:que_jobs].count.should be 1
304
304
  job = DB[:que_jobs].first
305
305
  job[:error_count].should be 1
306
- job[:last_error].should =~ /\AErrorJob!\n/
306
+ job[:last_error].should =~ /\AErrorJob!/
307
307
  job[:run_at].should be_within(3).of Time.now + 10
308
308
 
309
309
  DB[:que_jobs].update :error_count => 5,
@@ -317,14 +317,14 @@ describe Que::Job, '.work' do
317
317
  DB[:que_jobs].count.should be 1
318
318
  job = DB[:que_jobs].first
319
319
  job[:error_count].should be 6
320
- job[:last_error].should =~ /\AErrorJob!\n/
320
+ job[:last_error].should =~ /\AErrorJob!/
321
321
  job[:run_at].should be_within(3).of Time.now + 60
322
322
  end
323
323
 
324
- it "should pass it to an error handler, if one is defined" do
324
+ it "should pass it to an error notifier, if one is defined" do
325
325
  begin
326
326
  errors = []
327
- Que.error_handler = proc { |error| errors << error }
327
+ Que.error_notifier = proc { |error| errors << error }
328
328
 
329
329
  ErrorJob.enqueue
330
330
 
@@ -338,14 +338,14 @@ describe Que::Job, '.work' do
338
338
  error.should be_an_instance_of RuntimeError
339
339
  error.message.should == "ErrorJob!"
340
340
  ensure
341
- Que.error_handler = nil
341
+ Que.error_notifier = nil
342
342
  end
343
343
  end
344
344
 
345
- it "should pass job to an error handler, if one is defined" do
345
+ it "should pass job to an error notifier, if one is defined" do
346
346
  begin
347
347
  jobs = []
348
- Que.error_handler = proc { |error, job| jobs << job }
348
+ Que.error_notifier = proc { |error, job| jobs << job }
349
349
 
350
350
  ErrorJob.enqueue
351
351
  result = Que::Job.work
@@ -354,20 +354,20 @@ describe Que::Job, '.work' do
354
354
  job = jobs[0]
355
355
  job.should be result[:job]
356
356
  ensure
357
- Que.error_handler = nil
357
+ Que.error_notifier = nil
358
358
  end
359
359
  end
360
360
 
361
- it "should not do anything if the error handler itelf throws an error" do
361
+ it "should not do anything if the error notifier itelf throws an error" do
362
362
  begin
363
- Que.error_handler = proc { |error| raise "Another error!" }
363
+ Que.error_notifier = proc { |error| raise "Another error!" }
364
364
  ErrorJob.enqueue
365
365
 
366
366
  result = Que::Job.work
367
367
  result[:event].should == :job_errored
368
368
  result[:error].should be_an_instance_of RuntimeError
369
369
  ensure
370
- Que.error_handler = nil
370
+ Que.error_notifier = nil
371
371
  end
372
372
  end
373
373
 
@@ -403,5 +403,151 @@ describe Que::Job, '.work' do
403
403
  job[:error_count].should be 1
404
404
  job[:run_at].should be_within(3).of Time.now + 4
405
405
  end
406
+
407
+ context "in a job class that has a custom error handler" do
408
+ it "should allow it to schedule a retry after a specific interval" do
409
+ begin
410
+ error = nil
411
+ Que.error_notifier = proc { |e| error = e }
412
+
413
+ class CustomRetryIntervalJob < Que::Job
414
+ def run(*args)
415
+ raise "Blah!"
416
+ end
417
+
418
+ private
419
+
420
+ def handle_error(error)
421
+ retry_in(42)
422
+ end
423
+ end
424
+
425
+ CustomRetryIntervalJob.enqueue
426
+
427
+ result = Que::Job.work
428
+ result[:event].should == :job_errored
429
+ result[:error].should be_an_instance_of RuntimeError
430
+ result[:job][:job_class].should == 'CustomRetryIntervalJob'
431
+
432
+ DB[:que_jobs].count.should be 1
433
+ job = DB[:que_jobs].first
434
+ job[:error_count].should be 1
435
+ job[:last_error].should =~ /\ABlah!/
436
+ job[:run_at].should be_within(3).of Time.now + 42
437
+
438
+ error.should == result[:error]
439
+ ensure
440
+ Que.error_notifier = nil
441
+ end
442
+ end
443
+
444
+ it "should allow it to destroy the job" do
445
+ begin
446
+ error = nil
447
+ Que.error_notifier = proc { |e| error = e }
448
+
449
+ class CustomRetryIntervalJob < Que::Job
450
+ def run(*args)
451
+ raise "Blah!"
452
+ end
453
+
454
+ private
455
+
456
+ def handle_error(error)
457
+ destroy
458
+ end
459
+ end
460
+
461
+ CustomRetryIntervalJob.enqueue
462
+
463
+ result = Que::Job.work
464
+ result[:event].should == :job_errored
465
+ result[:error].should be_an_instance_of RuntimeError
466
+ result[:job][:job_class].should == 'CustomRetryIntervalJob'
467
+
468
+ DB[:que_jobs].count.should be 0
469
+
470
+ error.should == result[:error]
471
+ ensure
472
+ Que.error_notifier = nil
473
+ end
474
+ end
475
+
476
+ it "should allow it to return false to skip the error notification" do
477
+ begin
478
+ error = nil
479
+ Que.error_notifier = proc { |e| error = e }
480
+
481
+ class CustomRetryIntervalJob < Que::Job
482
+ def run(*args)
483
+ raise "Blah!"
484
+ end
485
+
486
+ private
487
+
488
+ def handle_error(error)
489
+ false
490
+ end
491
+ end
492
+
493
+ CustomRetryIntervalJob.enqueue
494
+
495
+ result = Que::Job.work
496
+ result[:event].should == :job_errored
497
+ result[:error].should be_an_instance_of RuntimeError
498
+ result[:job][:job_class].should == 'CustomRetryIntervalJob'
499
+
500
+ DB[:que_jobs].count.should be 0
501
+
502
+ error.should == nil
503
+ ensure
504
+ Que.error_notifier = nil
505
+ end
506
+ end
507
+
508
+ it "should allow it to call super to get the default behavior" do
509
+ begin
510
+ error = nil
511
+ Que.error_notifier = proc { |e| error = e }
512
+
513
+ class CustomRetryIntervalJob < Que::Job
514
+ def run(*args)
515
+ raise "Blah!"
516
+ end
517
+
518
+ private
519
+
520
+ def handle_error(error)
521
+ case error
522
+ when RuntimeError
523
+ super
524
+ else
525
+ $error_handler_failed = true
526
+ raise "Bad!"
527
+ end
528
+ end
529
+ end
530
+
531
+ CustomRetryIntervalJob.enqueue
532
+
533
+ result = Que::Job.work
534
+ result[:event].should == :job_errored
535
+ result[:error].should be_an_instance_of RuntimeError
536
+ result[:job][:job_class].should == 'CustomRetryIntervalJob'
537
+
538
+ $error_handler_failed.should == nil
539
+
540
+ DB[:que_jobs].count.should be 1
541
+ job = DB[:que_jobs].first
542
+ job[:error_count].should be 1
543
+ job[:last_error].should =~ /\ABlah!/
544
+ job[:run_at].should be_within(3).of Time.now + 4
545
+
546
+ error.should == result[:error]
547
+ ensure
548
+ Que.error_notifier = nil
549
+ end
550
+ end
551
+ end
406
552
  end
407
553
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: que
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.6
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Hanks
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-01 00:00:00.000000000 Z
11
+ date: 2016-09-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler