good_job 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -2
- data/README.md +66 -6
- data/lib/good_job.rb +1 -0
- data/lib/good_job/cli.rb +1 -7
- data/lib/good_job/job.rb +24 -23
- data/lib/good_job/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fc7b3997aaa38583efb4c91e66a2a5f9b8bfd75d4c14c9baaf8f1af7c236a42e
|
4
|
+
data.tar.gz: 285b441a5e5add440142960884370a1b74168266252fa48ce569cbb56e80f240
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f54e83d6ac19d80da2123a395ccf6ffd7eb2faab8e68bd5d50a572c8f0bf64fbc907125a47b7ff91dccac5497af113d043bba4f62ee6ae10fecaf3c004a1114d
|
7
|
+
data.tar.gz: 0b65a60bde26db0cfcfa55f166e80043bcbccb6e2c80f5de1840d2deae3ee28880c904bb53ffc2478ad834ffef8cfe567cee9a179932e20e4f92fdf8504838e4
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,23 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## [v1.0.
|
3
|
+
## [v1.0.3](https://github.com/bensheldon/good_job/tree/v1.0.3) (2020-07-25)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v1.0.2...v1.0.3)
|
6
|
+
|
7
|
+
**Fixed bugs:**
|
8
|
+
|
9
|
+
- Preserve GoodJob::Jobs when a StandardError is raised [\#60](https://github.com/bensheldon/good_job/issues/60)
|
10
|
+
|
11
|
+
**Closed issues:**
|
12
|
+
|
13
|
+
- Have an initial setup generator [\#6](https://github.com/bensheldon/good_job/issues/6)
|
14
|
+
|
15
|
+
**Merged pull requests:**
|
16
|
+
|
17
|
+
- Re-perform a job if a StandardError bubbles up; better document job reliability [\#62](https://github.com/bensheldon/good_job/pull/62) ([bensheldon](https://github.com/bensheldon))
|
18
|
+
- Update the setup documentation to use correct bin setup command [\#61](https://github.com/bensheldon/good_job/pull/61) ([jm96441n](https://github.com/jm96441n))
|
19
|
+
|
20
|
+
## [v1.0.2](https://github.com/bensheldon/good_job/tree/v1.0.2) (2020-07-25)
|
4
21
|
|
5
22
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v1.0.1...v1.0.2)
|
6
23
|
|
@@ -96,6 +113,7 @@
|
|
96
113
|
- Add configuration options to good\_job executable [\#33](https://github.com/bensheldon/good_job/pull/33) ([bensheldon](https://github.com/bensheldon))
|
97
114
|
- Extract Job querying behavior out of Scheduler [\#31](https://github.com/bensheldon/good_job/pull/31) ([bensheldon](https://github.com/bensheldon))
|
98
115
|
- Allow configuration of Rails queue adapter with `:good\_job` [\#28](https://github.com/bensheldon/good_job/pull/28) ([bensheldon](https://github.com/bensheldon))
|
116
|
+
- Update development Ruby to 2.6.5 [\#22](https://github.com/bensheldon/good_job/pull/22) ([bensheldon](https://github.com/bensheldon))
|
99
117
|
|
100
118
|
## [v0.5.0](https://github.com/bensheldon/good_job/tree/v0.5.0) (2020-07-13)
|
101
119
|
|
@@ -120,7 +138,6 @@
|
|
120
138
|
|
121
139
|
**Merged pull requests:**
|
122
140
|
|
123
|
-
- Update development Ruby to 2.6.5 [\#22](https://github.com/bensheldon/good_job/pull/22) ([bensheldon](https://github.com/bensheldon))
|
124
141
|
- Simplify the internal API, removing JobWrapper and InlineScheduler [\#21](https://github.com/bensheldon/good_job/pull/21) ([bensheldon](https://github.com/bensheldon))
|
125
142
|
- Generate a new future for every executed job [\#20](https://github.com/bensheldon/good_job/pull/20) ([bensheldon](https://github.com/bensheldon))
|
126
143
|
- Configuration for maximum number of job execution threads [\#18](https://github.com/bensheldon/good_job/pull/18) ([bensheldon](https://github.com/bensheldon))
|
data/README.md
CHANGED
@@ -79,25 +79,85 @@ $ bundle install
|
|
79
79
|
# [--poll-interval=N] # Interval between polls for available jobs in seconds (default: 1)
|
80
80
|
```
|
81
81
|
|
82
|
-
###
|
82
|
+
### Error handling, retries, and reliability
|
83
83
|
|
84
|
-
|
84
|
+
GoodJob guarantees _at-least-once_ performance of jobs. GoodJob fully supports ActiveJob's built-in functionality for error handling, retries and timeouts.
|
85
|
+
|
86
|
+
#### Error handling
|
87
|
+
|
88
|
+
By default, if a job raises an error while it is being performed, _and it bubbles up to the GoodJob backend_, GoodJob will be immediately re-perform the job until it finishes successfully.
|
89
|
+
|
90
|
+
- `Exception`-type errors, such as a SIGINT, will always cause a job to be re-performed.
|
91
|
+
- `StandardError`-type errors, by default, will cause a job to be re-performed, though this is configurable:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
# config/initializers/good_job.rb
|
95
|
+
GoodJob.reperform_jobs_on_standard_error = true # => default
|
96
|
+
```
|
97
|
+
|
98
|
+
### Retrying jobs
|
99
|
+
|
100
|
+
ActiveJob can be configured to retry an infinite number of times, with an exponential backoff. Using ActiveJob's `retry_on` will ensure that errors do not bubble up to the GoodJob backend:
|
85
101
|
|
86
102
|
```ruby
|
87
103
|
class ApplicationJob < ActiveJob::Base
|
88
|
-
# Retry errors an infinite number of times with exponential back-off
|
89
104
|
retry_on StandardError, wait: :exponentially_longer, attempts: Float::INFINITY
|
105
|
+
# ...
|
106
|
+
end
|
107
|
+
```
|
108
|
+
|
109
|
+
When specifying a limited number of retries, care must be taken to ensure that an error does not bubble up to the GoodJob backend because that will result in the job being re-performed:
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
class ApplicationJob < ActiveJob::Base
|
113
|
+
retry_on StandardError, attempts: 5 do |_job, _exception|
|
114
|
+
# Log error, etc.
|
115
|
+
# You must implement this block, otherwise,
|
116
|
+
# Active Job will re-raise the error.
|
117
|
+
# Do not re-raise the error, otherwise
|
118
|
+
# GoodJob will immediately re-perform the job.
|
119
|
+
end
|
120
|
+
# ...
|
121
|
+
end
|
122
|
+
```
|
123
|
+
|
124
|
+
GoodJob can be configured to allow omitting `retry_on`'s block argument and implicitly discard un-handled errors:
|
90
125
|
|
91
|
-
|
126
|
+
```ruby
|
127
|
+
# config/initializers/good_job.rb
|
128
|
+
|
129
|
+
# Do NOT re-perform a job if a StandardError bubbles up to the GoodJob backend
|
130
|
+
GoodJob.reperform_jobs_on_standard_error = false
|
131
|
+
```
|
132
|
+
|
133
|
+
ActiveJob's `discard_on` functionality is supported too.
|
134
|
+
|
135
|
+
#### ActionMailer retries
|
136
|
+
|
137
|
+
Using a Mailer's `#deliver_later` will enqueue an instance of `ActionMailer::DeliveryJob` which inherits from `ActiveJob::Base` rather than your applications `ApplicationJob`. You can use an initializer to configure retries on `ActionMailer::DeliveryJob`:
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
# config/initializers/good_job.rb
|
141
|
+
ActionMailer::DeliveryJob.retry_on StandardError, wait: :exponentially_longer, attempts: Float::INFINITY
|
142
|
+
```
|
143
|
+
|
144
|
+
#### Timeouts
|
145
|
+
|
146
|
+
Job timeouts can be configured with an `around_perform`:
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
class ApplicationJob < ActiveJob::Base
|
92
150
|
JobTimeoutError = Class.new(StandardError)
|
151
|
+
|
93
152
|
around_perform do |_job, block|
|
153
|
+
# Timeout jobs after 10 minutes
|
94
154
|
Timeout.timeout(10.minutes, JobTimeoutError) do
|
95
155
|
block.call
|
96
156
|
end
|
97
157
|
end
|
98
158
|
end
|
99
159
|
```
|
100
|
-
|
160
|
+
|
101
161
|
### Configuring Job Execution Threads
|
102
162
|
|
103
163
|
GoodJob executes enqueued jobs using threads. There is a lot than can be said about [multithreaded behavior in Ruby on Rails](https://guides.rubyonrails.org/threading_and_code_execution.html), but briefly:
|
@@ -167,7 +227,7 @@ To run tests:
|
|
167
227
|
$ git clone git@github.com:bensheldon/good_job.git
|
168
228
|
|
169
229
|
# Set up the local environment
|
170
|
-
$ bin/
|
230
|
+
$ bin/setup
|
171
231
|
|
172
232
|
# Run the tests
|
173
233
|
$ bin/rspec
|
data/lib/good_job.rb
CHANGED
@@ -13,6 +13,7 @@ require 'active_job/queue_adapters/good_job_adapter'
|
|
13
13
|
|
14
14
|
module GoodJob
|
15
15
|
mattr_accessor :preserve_job_records, default: false
|
16
|
+
mattr_accessor :reperform_jobs_on_standard_error, default: true
|
16
17
|
include Logging
|
17
18
|
|
18
19
|
ActiveSupport.run_load_hooks(:good_job, self)
|
data/lib/good_job/cli.rb
CHANGED
@@ -39,13 +39,7 @@ module GoodJob
|
|
39
39
|
job_query = GoodJob::Job.all.priority_ordered
|
40
40
|
queue_names_without_all = queue_names.reject { |q| q == '*' }
|
41
41
|
job_query = job_query.where(queue_name: queue_names_without_all) unless queue_names_without_all.size.zero?
|
42
|
-
|
43
|
-
performer_method = if GoodJob.preserve_job_records
|
44
|
-
:perform_with_advisory_lock_and_preserve_job_records
|
45
|
-
else
|
46
|
-
:perform_with_advisory_lock_and_destroy_job_records
|
47
|
-
end
|
48
|
-
job_performer = GoodJob::Performer.new(job_query, performer_method)
|
42
|
+
job_performer = GoodJob::Performer.new(job_query, :perform_with_advisory_lock)
|
49
43
|
|
50
44
|
$stdout.puts "GoodJob worker starting with max_threads=#{max_threads} on queues=#{queue_names.join(',')}"
|
51
45
|
|
data/lib/good_job/job.rb
CHANGED
@@ -21,7 +21,7 @@ module GoodJob
|
|
21
21
|
scope :priority_ordered, -> { order(priority: :desc) }
|
22
22
|
scope :finished, ->(timestamp = nil) { timestamp ? where(arel_table['finished_at'].lteq(timestamp)) : where.not(finished_at: nil) }
|
23
23
|
|
24
|
-
def self.perform_with_advisory_lock
|
24
|
+
def self.perform_with_advisory_lock
|
25
25
|
good_job = nil
|
26
26
|
result = nil
|
27
27
|
error = nil
|
@@ -30,20 +30,12 @@ module GoodJob
|
|
30
30
|
good_job = good_jobs.first
|
31
31
|
break unless good_job
|
32
32
|
|
33
|
-
result, error = good_job.perform
|
33
|
+
result, error = good_job.perform
|
34
34
|
end
|
35
35
|
|
36
36
|
[good_job, result, error] if good_job
|
37
37
|
end
|
38
38
|
|
39
|
-
def self.perform_with_advisory_lock_and_preserve_job_records
|
40
|
-
perform_with_advisory_lock(destroy_after: false)
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.perform_with_advisory_lock_and_destroy_job_records
|
44
|
-
perform_with_advisory_lock(destroy_after: true)
|
45
|
-
end
|
46
|
-
|
47
39
|
def self.enqueue(active_job, scheduled_at: nil, create_with_advisory_lock: false)
|
48
40
|
good_job = nil
|
49
41
|
ActiveSupport::Notifications.instrument("enqueue_job.good_job", { active_job: active_job, scheduled_at: scheduled_at, create_with_advisory_lock: create_with_advisory_lock }) do |instrument_payload|
|
@@ -64,40 +56,49 @@ module GoodJob
|
|
64
56
|
good_job
|
65
57
|
end
|
66
58
|
|
67
|
-
def perform(destroy_after:
|
59
|
+
def perform(destroy_after: !GoodJob.preserve_job_records, reperform_on_standard_error: GoodJob.reperform_jobs_on_standard_error)
|
68
60
|
raise PreviouslyPerformedError, 'Cannot perform a job that has already been performed' if finished_at
|
69
61
|
|
70
62
|
result = nil
|
63
|
+
rescued_error = nil
|
71
64
|
error = nil
|
72
65
|
|
73
66
|
ActiveSupport::Notifications.instrument("before_perform_job.good_job", { good_job: self })
|
74
67
|
self.performed_at = Time.current
|
75
68
|
save! unless destroy_after
|
76
69
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
70
|
+
params = serialized_params.merge(
|
71
|
+
"provider_job_id" => id
|
72
|
+
)
|
73
|
+
|
74
|
+
begin
|
75
|
+
ActiveSupport::Notifications.instrument("perform_job.good_job", { good_job: self }) do
|
82
76
|
result = ActiveJob::Base.execute(params)
|
83
|
-
rescue StandardError => e
|
84
|
-
error = e
|
85
77
|
end
|
78
|
+
rescue StandardError => e
|
79
|
+
rescued_error = e
|
86
80
|
end
|
87
81
|
|
88
|
-
if
|
82
|
+
if rescued_error
|
83
|
+
error = rescued_error
|
84
|
+
elsif result.is_a?(Exception)
|
89
85
|
error = result
|
90
86
|
result = nil
|
91
87
|
end
|
92
88
|
|
93
89
|
error_message = "#{error.class}: #{error.message}" if error
|
94
90
|
self.error = error_message
|
95
|
-
self.finished_at = Time.current
|
96
91
|
|
97
|
-
if
|
98
|
-
destroy!
|
99
|
-
else
|
92
|
+
if rescued_error && reperform_on_standard_error
|
100
93
|
save!
|
94
|
+
else
|
95
|
+
self.finished_at = Time.current
|
96
|
+
|
97
|
+
if destroy_after
|
98
|
+
destroy!
|
99
|
+
else
|
100
|
+
save!
|
101
|
+
end
|
101
102
|
end
|
102
103
|
|
103
104
|
[result, error]
|
data/lib/good_job/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: good_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Sheldon
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-07-
|
11
|
+
date: 2020-07-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|