que 0.11.6 → 0.12.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.
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