good_job 1.1.2 → 1.1.3
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 +14 -4
- data/lib/good_job.rb +1 -0
- data/lib/good_job/current_execution.rb +25 -0
- data/lib/good_job/job.rb +6 -1
- data/lib/good_job/railtie.rb +10 -0
- data/lib/good_job/scheduler.rb +55 -14
- data/lib/good_job/version.rb +1 -1
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 44da5dd6eb7dee7e08319f7405e97f81e4223f8c88d48ba4a0512fa006f89e78
|
4
|
+
data.tar.gz: fe3f5ca8a39b85b1aa4be77a819e910691223cf36e80c6ab337ff1b224a26e16
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e6648e41c7ff99915716702651cd43fb7fffcff24528a9fb2d7e4a7913303d85d25c3b3f8ef54cc1b716a273836bc3c7fb2911750e1d99f216b2074b03f13ae8
|
7
|
+
data.tar.gz: '063048f09b76b10d4f38beb5fcf390d972f0952ed4df7b1053e8078da2bad005b816e21271026b95f8eaef12b8e3e1ce0a4cecf2cf40f965e852a8e2e9854aac'
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v1.1.3](https://github.com/bensheldon/good_job/tree/v1.1.3) (2020-08-14)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v1.1.2...v1.1.3)
|
6
|
+
|
7
|
+
**Fixed bugs:**
|
8
|
+
|
9
|
+
- Job exceptions not properly attached to good\_jobs record [\#72](https://github.com/bensheldon/good_job/issues/72)
|
10
|
+
|
11
|
+
**Merged pull requests:**
|
12
|
+
|
13
|
+
- Capture errors via instrumentation from retry\_on and discard\_on [\#79](https://github.com/bensheldon/good_job/pull/79) ([bensheldon](https://github.com/bensheldon))
|
14
|
+
- Document GoodJob::Scheduler with Yard [\#78](https://github.com/bensheldon/good_job/pull/78) ([bensheldon](https://github.com/bensheldon))
|
15
|
+
|
3
16
|
## [v1.1.2](https://github.com/bensheldon/good_job/tree/v1.1.2) (2020-08-13)
|
4
17
|
|
5
18
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v1.1.1...v1.1.2)
|
@@ -60,6 +73,7 @@
|
|
60
73
|
|
61
74
|
- 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))
|
62
75
|
- Update the setup documentation to use correct bin setup command [\#61](https://github.com/bensheldon/good_job/pull/61) ([jm96441n](https://github.com/jm96441n))
|
76
|
+
- Allow preservation of finished job records [\#46](https://github.com/bensheldon/good_job/pull/46) ([bensheldon](https://github.com/bensheldon))
|
63
77
|
|
64
78
|
## [v1.0.2](https://github.com/bensheldon/good_job/tree/v1.0.2) (2020-07-25)
|
65
79
|
|
@@ -90,10 +104,6 @@
|
|
90
104
|
|
91
105
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v0.8.2...v0.9.0)
|
92
106
|
|
93
|
-
**Merged pull requests:**
|
94
|
-
|
95
|
-
- Allow preservation of finished job records [\#46](https://github.com/bensheldon/good_job/pull/46) ([bensheldon](https://github.com/bensheldon))
|
96
|
-
|
97
107
|
## [v0.8.2](https://github.com/bensheldon/good_job/tree/v0.8.2) (2020-07-18)
|
98
108
|
|
99
109
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v0.8.1...v0.8.2)
|
data/lib/good_job.rb
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
module GoodJob
|
2
|
+
# Thread-local attributes for passing values from Instrumentation.
|
3
|
+
# (Cannot use ActiveSupport::CurrentAttributes because ActiveJob resets it)
|
4
|
+
|
5
|
+
module CurrentExecution
|
6
|
+
# @!attribute [rw] error_on_retry
|
7
|
+
# @!scope class
|
8
|
+
# Error captured by retry_on
|
9
|
+
# @return [Exception, nil]
|
10
|
+
thread_mattr_accessor :error_on_retry
|
11
|
+
|
12
|
+
# @!attribute [rw] error_on_discard
|
13
|
+
# @!scope class
|
14
|
+
# Error captured by discard_on
|
15
|
+
# @return [Exception, nil]
|
16
|
+
thread_mattr_accessor :error_on_discard
|
17
|
+
|
18
|
+
# Resets attributes
|
19
|
+
# @return [void]
|
20
|
+
def self.reset
|
21
|
+
self.error_on_retry = nil
|
22
|
+
self.error_on_discard = nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/good_job/job.rb
CHANGED
@@ -76,11 +76,11 @@ module GoodJob
|
|
76
76
|
def perform(destroy_after: !GoodJob.preserve_job_records, reperform_on_standard_error: GoodJob.reperform_jobs_on_standard_error)
|
77
77
|
raise PreviouslyPerformedError, 'Cannot perform a job that has already been performed' if finished_at
|
78
78
|
|
79
|
+
GoodJob::CurrentExecution.reset
|
79
80
|
result = nil
|
80
81
|
rescued_error = nil
|
81
82
|
error = nil
|
82
83
|
|
83
|
-
ActiveSupport::Notifications.instrument("before_perform_job.good_job", { good_job: self })
|
84
84
|
self.performed_at = Time.current
|
85
85
|
save! unless destroy_after
|
86
86
|
|
@@ -96,11 +96,16 @@ module GoodJob
|
|
96
96
|
rescued_error = e
|
97
97
|
end
|
98
98
|
|
99
|
+
retry_or_discard_error = GoodJob::CurrentExecution.error_on_retry ||
|
100
|
+
GoodJob::CurrentExecution.error_on_discard
|
101
|
+
|
99
102
|
if rescued_error
|
100
103
|
error = rescued_error
|
101
104
|
elsif result.is_a?(Exception)
|
102
105
|
error = result
|
103
106
|
result = nil
|
107
|
+
elsif retry_or_discard_error
|
108
|
+
error = retry_or_discard_error
|
104
109
|
end
|
105
110
|
|
106
111
|
error_message = "#{error.class}: #{error.message}" if error
|
data/lib/good_job/railtie.rb
CHANGED
@@ -4,5 +4,15 @@ module GoodJob
|
|
4
4
|
ActiveSupport.on_load(:good_job) { self.logger = ::Rails.logger }
|
5
5
|
GoodJob::LogSubscriber.attach_to :good_job
|
6
6
|
end
|
7
|
+
|
8
|
+
initializer "good_job.active_job_notifications" do
|
9
|
+
ActiveSupport::Notifications.subscribe "enqueue_retry.active_job" do |event|
|
10
|
+
GoodJob::CurrentExecution.error_on_retry = event.payload[:error]
|
11
|
+
end
|
12
|
+
|
13
|
+
ActiveSupport::Notifications.subscribe "discard.active_job" do |event|
|
14
|
+
GoodJob::CurrentExecution.error_on_discard = event.payload[:error]
|
15
|
+
end
|
16
|
+
end
|
7
17
|
end
|
8
18
|
end
|
data/lib/good_job/scheduler.rb
CHANGED
@@ -2,14 +2,22 @@ require "concurrent/executor/thread_pool_executor"
|
|
2
2
|
require "concurrent/timer_task"
|
3
3
|
require "concurrent/utility/processor_counter"
|
4
4
|
|
5
|
-
module GoodJob
|
5
|
+
module GoodJob # :nodoc:
|
6
|
+
# Schedulers are generic thread execution pools that are responsible for
|
7
|
+
# periodically checking for available execution tasks, executing tasks in a
|
8
|
+
# bounded thread-pool, and efficiently scaling execution threads.
|
9
|
+
#
|
10
|
+
# Schedulers are "generic" in the sense that they delegate task execution
|
11
|
+
# details to a "Performer" object that responds to #next.
|
6
12
|
class Scheduler
|
13
|
+
# Defaults for instance of Concurrent::TimerTask
|
7
14
|
DEFAULT_TIMER_OPTIONS = {
|
8
15
|
execution_interval: 1,
|
9
16
|
timeout_interval: 1,
|
10
17
|
run_now: true,
|
11
18
|
}.freeze
|
12
19
|
|
20
|
+
# Defaults for instance of Concurrent::ThreadPoolExecutor
|
13
21
|
DEFAULT_POOL_OPTIONS = {
|
14
22
|
name: 'good_job',
|
15
23
|
min_threads: 0,
|
@@ -20,8 +28,15 @@ module GoodJob
|
|
20
28
|
fallback_policy: :discard,
|
21
29
|
}.freeze
|
22
30
|
|
31
|
+
# @!attribute [r] instances
|
32
|
+
# @!scope class
|
33
|
+
# All instantiated Schedulers in the current process.
|
34
|
+
# @return [array<GoodJob:Scheduler>]
|
23
35
|
cattr_reader :instances, default: [], instance_reader: false
|
24
36
|
|
37
|
+
# Creates GoodJob::Scheduler(s) and Performers from a GoodJob::Configuration instance.
|
38
|
+
# @param configuration [GoodJob::Configuration]
|
39
|
+
# @return [GoodJob::Scheduler, GoodJob::MultiScheduler]
|
25
40
|
def self.from_configuration(configuration)
|
26
41
|
schedulers = configuration.queue_string.split(';').map do |queue_string_and_max_threads|
|
27
42
|
queue_string, max_threads = queue_string_and_max_threads.split(':')
|
@@ -47,6 +62,9 @@ module GoodJob
|
|
47
62
|
end
|
48
63
|
end
|
49
64
|
|
65
|
+
# @param performer [GoodJob::Performer]
|
66
|
+
# @param timer_options [Hash] Options to instantiate a Concurrent::TimerTask
|
67
|
+
# @param pool_options [Hash] Options to instantiate a Concurrent::ThreadPoolExecutor
|
50
68
|
def initialize(performer, timer_options: {}, pool_options: {})
|
51
69
|
raise ArgumentError, "Performer argument must implement #next" unless performer.respond_to?(:next)
|
52
70
|
|
@@ -59,6 +77,9 @@ module GoodJob
|
|
59
77
|
create_pools
|
60
78
|
end
|
61
79
|
|
80
|
+
# Shut down the Scheduler.
|
81
|
+
# @param wait [Boolean] Wait for actively executing jobs to finish
|
82
|
+
# @return [void]
|
62
83
|
def shutdown(wait: true)
|
63
84
|
@_shutdown = true
|
64
85
|
|
@@ -76,10 +97,15 @@ module GoodJob
|
|
76
97
|
end
|
77
98
|
end
|
78
99
|
|
100
|
+
# True when the Scheduler is shutdown.
|
101
|
+
# @return [true, false, nil]
|
79
102
|
def shutdown?
|
80
103
|
@_shutdown
|
81
104
|
end
|
82
105
|
|
106
|
+
# Restart the Scheduler. When shutdown, start; or shutdown and start.
|
107
|
+
# @param wait [Boolean] Wait for actively executing jobs to finish
|
108
|
+
# @return [void]
|
83
109
|
def restart(wait: true)
|
84
110
|
ActiveSupport::Notifications.instrument("scheduler_restart_pools.good_job", { process_id: process_id }) do
|
85
111
|
shutdown(wait: wait) unless shutdown?
|
@@ -87,6 +113,8 @@ module GoodJob
|
|
87
113
|
end
|
88
114
|
end
|
89
115
|
|
116
|
+
# Triggers the execution the Performer, if an execution thread is available.
|
117
|
+
# @return [Boolean]
|
90
118
|
def create_thread
|
91
119
|
return false unless @pool.ready_worker_count.positive?
|
92
120
|
|
@@ -97,33 +125,29 @@ module GoodJob
|
|
97
125
|
end
|
98
126
|
future.add_observer(self, :task_observer)
|
99
127
|
future.execute
|
128
|
+
true
|
100
129
|
end
|
101
130
|
|
131
|
+
# Invoked on completion of TimerTask task.
|
132
|
+
# @!visibility private
|
133
|
+
# @return [void]
|
102
134
|
def timer_observer(time, executed_task, thread_error)
|
103
135
|
GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
|
104
136
|
ActiveSupport::Notifications.instrument("finished_timer_task.good_job", { result: executed_task, error: thread_error, time: time })
|
105
137
|
end
|
106
138
|
|
139
|
+
# Invoked on completion of ThreadPoolExecutor task
|
140
|
+
# @!visibility private
|
141
|
+
# @return [void]
|
107
142
|
def task_observer(time, output, thread_error)
|
108
143
|
GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
|
109
144
|
ActiveSupport::Notifications.instrument("finished_job_task.good_job", { result: output, error: thread_error, time: time })
|
110
145
|
create_thread if output
|
111
146
|
end
|
112
147
|
|
113
|
-
class ThreadPoolExecutor < Concurrent::ThreadPoolExecutor
|
114
|
-
# https://github.com/ruby-concurrency/concurrent-ruby/issues/684#issuecomment-427594437
|
115
|
-
def ready_worker_count
|
116
|
-
synchronize do
|
117
|
-
workers_still_to_be_created = @max_length - @pool.length
|
118
|
-
workers_created_but_waiting = @ready.length
|
119
|
-
|
120
|
-
workers_still_to_be_created + workers_created_but_waiting
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
148
|
private
|
126
149
|
|
150
|
+
# @return [void]
|
127
151
|
def create_pools
|
128
152
|
ActiveSupport::Notifications.instrument("scheduler_create_pools.good_job", { performer_name: @performer.name, max_threads: @pool_options[:max_threads], poll_interval: @timer_options[:execution_interval], process_id: process_id }) do
|
129
153
|
@pool = ThreadPoolExecutor.new(@pool_options)
|
@@ -135,12 +159,29 @@ module GoodJob
|
|
135
159
|
end
|
136
160
|
end
|
137
161
|
|
162
|
+
# @return [Integer] Current process ID
|
138
163
|
def process_id
|
139
164
|
Process.pid
|
140
165
|
end
|
141
166
|
|
167
|
+
# @return [String] Current thread name
|
142
168
|
def thread_name
|
143
|
-
Thread.current.name || Thread.current.object_id
|
169
|
+
(Thread.current.name || Thread.current.object_id).to_s
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Slightly customized sub-class of Concurrent::ThreadPoolExecutor
|
174
|
+
class ThreadPoolExecutor < Concurrent::ThreadPoolExecutor
|
175
|
+
# Number of idle or potential threads available to execute tasks
|
176
|
+
# https://github.com/ruby-concurrency/concurrent-ruby/issues/684#issuecomment-427594437
|
177
|
+
# @return [Integer]
|
178
|
+
def ready_worker_count
|
179
|
+
synchronize do
|
180
|
+
workers_still_to_be_created = @max_length - @pool.length
|
181
|
+
workers_created_but_waiting = @ready.length
|
182
|
+
|
183
|
+
workers_still_to_be_created + workers_created_but_waiting
|
184
|
+
end
|
144
185
|
end
|
145
186
|
end
|
146
187
|
end
|
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.1.
|
4
|
+
version: 1.1.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-08-
|
11
|
+
date: 2020-08-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -248,6 +248,20 @@ dependencies:
|
|
248
248
|
- - ">="
|
249
249
|
- !ruby/object:Gem::Version
|
250
250
|
version: '0'
|
251
|
+
- !ruby/object:Gem::Dependency
|
252
|
+
name: yard
|
253
|
+
requirement: !ruby/object:Gem::Requirement
|
254
|
+
requirements:
|
255
|
+
- - ">="
|
256
|
+
- !ruby/object:Gem::Version
|
257
|
+
version: '0'
|
258
|
+
type: :development
|
259
|
+
prerelease: false
|
260
|
+
version_requirements: !ruby/object:Gem::Requirement
|
261
|
+
requirements:
|
262
|
+
- - ">="
|
263
|
+
- !ruby/object:Gem::Version
|
264
|
+
version: '0'
|
251
265
|
description: A multithreaded, Postgres-based ActiveJob backend for Ruby on Rails
|
252
266
|
email:
|
253
267
|
- bensheldon@gmail.com
|
@@ -270,6 +284,7 @@ files:
|
|
270
284
|
- lib/good_job/adapter.rb
|
271
285
|
- lib/good_job/cli.rb
|
272
286
|
- lib/good_job/configuration.rb
|
287
|
+
- lib/good_job/current_execution.rb
|
273
288
|
- lib/good_job/job.rb
|
274
289
|
- lib/good_job/lockable.rb
|
275
290
|
- lib/good_job/log_subscriber.rb
|