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 +4 -4
- data/CHANGELOG.md +7 -1
- data/docs/error_handling.md +2 -2
- data/lib/que.rb +11 -1
- data/lib/que/job.rb +37 -7
- data/lib/que/sql.rb +7 -7
- data/lib/que/version.rb +1 -1
- data/spec/unit/work_spec.rb +161 -15
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd0f74eb9d94a2f949174b802fdd70f898cfe2e3
|
4
|
+
data.tar.gz: 60c5ea6a16dd1080bc16fcea19250c01e94937f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
|
data/docs/error_handling.md
CHANGED
@@ -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
|
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.
|
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 :
|
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)
|
103
|
-
|
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, [
|
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.
|
123
|
-
# Similarly, protect the work loop from a failure of the error
|
124
|
-
Que.
|
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 =
|
92
|
-
run_at = now() + $
|
93
|
-
last_error = $
|
94
|
-
WHERE queue = $
|
95
|
-
AND priority = $
|
96
|
-
AND run_at = $
|
97
|
-
AND job_id = $
|
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
data/spec/unit/work_spec.rb
CHANGED
@@ -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
|
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
|
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
|
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
|
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
|
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
|
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
|
324
|
+
it "should pass it to an error notifier, if one is defined" do
|
325
325
|
begin
|
326
326
|
errors = []
|
327
|
-
Que.
|
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.
|
341
|
+
Que.error_notifier = nil
|
342
342
|
end
|
343
343
|
end
|
344
344
|
|
345
|
-
it "should pass job to an error
|
345
|
+
it "should pass job to an error notifier, if one is defined" do
|
346
346
|
begin
|
347
347
|
jobs = []
|
348
|
-
Que.
|
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.
|
357
|
+
Que.error_notifier = nil
|
358
358
|
end
|
359
359
|
end
|
360
360
|
|
361
|
-
it "should not do anything if the error
|
361
|
+
it "should not do anything if the error notifier itelf throws an error" do
|
362
362
|
begin
|
363
|
-
Que.
|
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.
|
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.
|
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-
|
11
|
+
date: 2016-09-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|