good_job 1.3.0 → 1.3.1
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 +31 -1
- data/engine/app/controllers/good_job/dashboards_controller.rb +1 -1
- data/engine/app/views/shared/_jobs_table.erb +2 -2
- data/lib/good_job/adapter.rb +3 -0
- data/lib/good_job/cli.rb +4 -1
- data/lib/good_job/lockable.rb +7 -7
- data/lib/good_job/log_subscriber.rb +10 -10
- data/lib/good_job/notifier.rb +3 -3
- data/lib/good_job/poller.rb +94 -0
- data/lib/good_job/scheduler.rb +27 -62
- data/lib/good_job/version.rb +1 -1
- metadata +18 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ce89780b94c2a1f01cff7b85b3bdb066988a50c1b55ecef909d69ed0dbce5b00
|
4
|
+
data.tar.gz: e68b2a0b1283e79d30d8dd49e13a8f01fea53e7cf99d60aa9ab243826177838a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1332dd583ab8d0d1e359b85c0f03d082a2407f0ef825d419aae4986e003a18a9adfce9e253aaa5a386497b1a0fc941f732968caac3a813c78f079c23fae2bd72
|
7
|
+
data.tar.gz: 1650338a713a12387b15241eb5d8808ac9fbf43fa780645c1886612ba0590ca392f2dbd93e6c8a82e5483ddb3e3ba60d0c4e04695c6155ae3993d82af42bbef7
|
data/CHANGELOG.md
CHANGED
@@ -1,12 +1,42 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v1.3.1](https://github.com/bensheldon/good_job/tree/v1.3.1) (2020-11-01)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v1.3.0...v1.3.1)
|
6
|
+
|
7
|
+
**Implemented enhancements:**
|
8
|
+
|
9
|
+
- Extract polling from scheduler into Polling object [\#128](https://github.com/bensheldon/good_job/issues/128)
|
10
|
+
- Format serialized params to ease reading [\#170](https://github.com/bensheldon/good_job/pull/170) ([morgoth](https://github.com/morgoth))
|
11
|
+
|
12
|
+
**Fixed bugs:**
|
13
|
+
|
14
|
+
- Don't disconnect a nil activerecord connection [\#161](https://github.com/bensheldon/good_job/pull/161) ([bensheldon](https://github.com/bensheldon))
|
15
|
+
|
16
|
+
**Closed issues:**
|
17
|
+
|
18
|
+
- Propose addition of GoodJob to queue-shootout benchmarks [\#40](https://github.com/bensheldon/good_job/issues/40)
|
19
|
+
|
20
|
+
**Merged pull requests:**
|
21
|
+
|
22
|
+
- Ensure Rails is a development dependency [\#169](https://github.com/bensheldon/good_job/pull/169) ([bensheldon](https://github.com/bensheldon))
|
23
|
+
- Fix Ruby 2.7 GH action by setting default bundler explicitly [\#166](https://github.com/bensheldon/good_job/pull/166) ([bensheldon](https://github.com/bensheldon))
|
24
|
+
- Cache ruby version explicitly in Github Action [\#165](https://github.com/bensheldon/good_job/pull/165) ([bensheldon](https://github.com/bensheldon))
|
25
|
+
- Update development dependencies, rubocop [\#164](https://github.com/bensheldon/good_job/pull/164) ([bensheldon](https://github.com/bensheldon))
|
26
|
+
- Fix intended constant hierarchy of GoodJob::Scheduler::ThreadPoolExecutor [\#158](https://github.com/bensheldon/good_job/pull/158) ([bensheldon](https://github.com/bensheldon))
|
27
|
+
- Add bin/test\_app executable for Rails debugging [\#157](https://github.com/bensheldon/good_job/pull/157) ([bensheldon](https://github.com/bensheldon))
|
28
|
+
- Extract Scheduler polling behavior to its own object [\#152](https://github.com/bensheldon/good_job/pull/152) ([bensheldon](https://github.com/bensheldon))
|
29
|
+
|
3
30
|
## [v1.3.0](https://github.com/bensheldon/good_job/tree/v1.3.0) (2020-10-03)
|
4
31
|
|
5
32
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v1.2.6...v1.3.0)
|
6
33
|
|
7
|
-
**
|
34
|
+
**Implemented enhancements:**
|
8
35
|
|
9
36
|
- Lengthen default poll interval from 1 to 5 seconds [\#156](https://github.com/bensheldon/good_job/pull/156) ([bensheldon](https://github.com/bensheldon))
|
37
|
+
|
38
|
+
**Merged pull requests:**
|
39
|
+
|
10
40
|
- Rename reperform\_jobs\_on\_standard\_error to retry\_on\_unhandled\_error [\#154](https://github.com/bensheldon/good_job/pull/154) ([morgoth](https://github.com/morgoth))
|
11
41
|
|
12
42
|
## [v1.2.6](https://github.com/bensheldon/good_job/tree/v1.2.6) (2020-09-29)
|
@@ -4,7 +4,7 @@ module GoodJob
|
|
4
4
|
@jobs = GoodJob::Job.display_all(after_scheduled_at: params[:after_scheduled_at], after_id: params[:after_id])
|
5
5
|
.limit(params.fetch(:limit, 10))
|
6
6
|
|
7
|
-
job_data = GoodJob::Job.connection.exec_query Arel.sql(<<~SQL)
|
7
|
+
job_data = GoodJob::Job.connection.exec_query Arel.sql(<<~SQL.squish)
|
8
8
|
SELECT *
|
9
9
|
FROM generate_series(
|
10
10
|
date_trunc('hour', NOW() - '1 day'::interval),
|
@@ -6,8 +6,8 @@
|
|
6
6
|
<th>Job Class</th>
|
7
7
|
<th>Queue</th>
|
8
8
|
<th>Scheduled At</th>
|
9
|
-
<th>ActiveJob Params</th>
|
10
9
|
<th>Error</th>
|
10
|
+
<th>ActiveJob Params</th>
|
11
11
|
</thead>
|
12
12
|
<tbody>
|
13
13
|
<% jobs.each do |job| %>
|
@@ -17,8 +17,8 @@
|
|
17
17
|
<td><%= job.serialized_params['job_class'] %></td>
|
18
18
|
<td><%= job.queue_name %></td>
|
19
19
|
<td><%= job.scheduled_at || job.created_at %></td>
|
20
|
-
<td><%= job.serialized_params %></td>
|
21
20
|
<td><%= job.error %></td>
|
21
|
+
<td><pre><%= JSON.pretty_generate(job.serialized_params) %></pre></td>
|
22
22
|
</tr>
|
23
23
|
<% end %>
|
24
24
|
</tbody>
|
data/lib/good_job/adapter.rb
CHANGED
@@ -43,8 +43,10 @@ module GoodJob
|
|
43
43
|
|
44
44
|
if @execution_mode == :async # rubocop:disable Style/GuardClause
|
45
45
|
@notifier = notifier || GoodJob::Notifier.new
|
46
|
+
@poller = GoodJob::Poller.new(poll_interval: configuration.poll_interval)
|
46
47
|
@scheduler = scheduler || GoodJob::Scheduler.from_configuration(configuration)
|
47
48
|
@notifier.recipients << [@scheduler, :create_thread]
|
49
|
+
@poller.recipients << [@scheduler, :create_thread]
|
48
50
|
end
|
49
51
|
end
|
50
52
|
|
@@ -88,6 +90,7 @@ module GoodJob
|
|
88
90
|
# @return [void]
|
89
91
|
def shutdown(wait: true)
|
90
92
|
@notifier&.shutdown(wait: wait)
|
93
|
+
@poller&.shutdown(wait: wait)
|
91
94
|
@scheduler&.shutdown(wait: wait)
|
92
95
|
end
|
93
96
|
|
data/lib/good_job/cli.rb
CHANGED
@@ -45,11 +45,13 @@ module GoodJob
|
|
45
45
|
desc: "Interval between polls for available jobs in seconds (env var: GOOD_JOB_POLL_INTERVAL, default: 5)"
|
46
46
|
def start
|
47
47
|
set_up_application!
|
48
|
+
configuration = GoodJob::Configuration.new(options)
|
48
49
|
|
49
50
|
notifier = GoodJob::Notifier.new
|
50
|
-
|
51
|
+
poller = GoodJob::Poller.new(poll_interval: configuration.poll_interval)
|
51
52
|
scheduler = GoodJob::Scheduler.from_configuration(configuration)
|
52
53
|
notifier.recipients << [scheduler, :create_thread]
|
54
|
+
poller.recipients << [scheduler, :create_thread]
|
53
55
|
|
54
56
|
@stop_good_job_executable = false
|
55
57
|
%w[INT TERM].each do |signal|
|
@@ -62,6 +64,7 @@ module GoodJob
|
|
62
64
|
end
|
63
65
|
|
64
66
|
notifier.shutdown
|
67
|
+
poller.shutdown
|
65
68
|
scheduler.shutdown
|
66
69
|
end
|
67
70
|
|
data/lib/good_job/lockable.rb
CHANGED
@@ -56,7 +56,7 @@ module GoodJob
|
|
56
56
|
# @example Get the records that have a session awaiting a lock:
|
57
57
|
# MyLockableRecord.joins_advisory_locks.where("pg_locks.granted = ?", false)
|
58
58
|
scope :joins_advisory_locks, (lambda do
|
59
|
-
join_sql = <<~SQL
|
59
|
+
join_sql = <<~SQL.squish
|
60
60
|
LEFT JOIN pg_locks ON pg_locks.locktype = 'advisory'
|
61
61
|
AND pg_locks.objsubid = 1
|
62
62
|
AND pg_locks.classid = ('x' || substr(md5(:table_name || #{quoted_table_name}.#{quoted_primary_key}::text), 1, 16))::bit(32)::int
|
@@ -140,10 +140,10 @@ module GoodJob
|
|
140
140
|
# all remaining locks).
|
141
141
|
# @return [Boolean] whether the lock was acquired.
|
142
142
|
def advisory_lock
|
143
|
-
where_sql = <<~SQL
|
143
|
+
where_sql = <<~SQL.squish
|
144
144
|
pg_try_advisory_lock(('x' || substr(md5(:table_name || :id::text), 1, 16))::bit(64)::bigint)
|
145
145
|
SQL
|
146
|
-
self.class.unscoped.
|
146
|
+
self.class.unscoped.exists?([where_sql, { table_name: self.class.table_name, id: send(self.class.primary_key) }])
|
147
147
|
end
|
148
148
|
|
149
149
|
# Releases an advisory lock on this record if it is locked by this database
|
@@ -151,10 +151,10 @@ module GoodJob
|
|
151
151
|
# {#advisory_unlock} and {#advisory_lock} the same number of times.
|
152
152
|
# @return [Boolean] whether the lock was released.
|
153
153
|
def advisory_unlock
|
154
|
-
where_sql = <<~SQL
|
154
|
+
where_sql = <<~SQL.squish
|
155
155
|
pg_advisory_unlock(('x' || substr(md5(:table_name || :id::text), 1, 16))::bit(64)::bigint)
|
156
156
|
SQL
|
157
|
-
self.class.unscoped.
|
157
|
+
self.class.unscoped.exists?([where_sql, { table_name: self.class.table_name, id: send(self.class.primary_key) }])
|
158
158
|
end
|
159
159
|
|
160
160
|
# Acquires an advisory lock on this record or raises
|
@@ -191,13 +191,13 @@ module GoodJob
|
|
191
191
|
# Tests whether this record has an advisory lock on it.
|
192
192
|
# @return [Boolean]
|
193
193
|
def advisory_locked?
|
194
|
-
self.class.unscoped.advisory_locked.
|
194
|
+
self.class.unscoped.advisory_locked.exists?(id: send(self.class.primary_key))
|
195
195
|
end
|
196
196
|
|
197
197
|
# Tests whether this record is locked by the current database session.
|
198
198
|
# @return [Boolean]
|
199
199
|
def owns_advisory_lock?
|
200
|
-
self.class.unscoped.owns_advisory_locked.
|
200
|
+
self.class.unscoped.owns_advisory_locked.exists?(id: send(self.class.primary_key))
|
201
201
|
end
|
202
202
|
|
203
203
|
# Releases all advisory locks on the record that are held by the current
|
@@ -45,14 +45,13 @@ module GoodJob
|
|
45
45
|
end
|
46
46
|
|
47
47
|
# @macro notification_responder
|
48
|
-
def
|
48
|
+
def scheduler_create_pool(event)
|
49
49
|
max_threads = event.payload[:max_threads]
|
50
|
-
poll_interval = event.payload[:poll_interval]
|
51
50
|
performer_name = event.payload[:performer_name]
|
52
51
|
process_id = event.payload[:process_id]
|
53
52
|
|
54
53
|
info(tags: [process_id]) do
|
55
|
-
"GoodJob started scheduler with queues=#{performer_name} max_threads=#{max_threads}
|
54
|
+
"GoodJob started scheduler with queues=#{performer_name} max_threads=#{max_threads}."
|
56
55
|
end
|
57
56
|
end
|
58
57
|
|
@@ -166,12 +165,12 @@ module GoodJob
|
|
166
165
|
# @return [Logger]
|
167
166
|
def logger
|
168
167
|
@_logger ||= begin
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
168
|
+
logger = Logger.new(StringIO.new)
|
169
|
+
loggers.each do |each_logger|
|
170
|
+
logger.extend(ActiveSupport::Logger.broadcast(each_logger))
|
171
|
+
end
|
172
|
+
logger
|
173
|
+
end
|
175
174
|
end
|
176
175
|
|
177
176
|
# Reset {LogSubscriber.logger} and force it to rebuild a new shortcut to
|
@@ -192,11 +191,12 @@ module GoodJob
|
|
192
191
|
# @return [void]
|
193
192
|
def tag_logger(*tags, &block)
|
194
193
|
tags = tags.dup.unshift("GoodJob").compact
|
194
|
+
good_job_tag = ["ActiveJob"].freeze
|
195
195
|
|
196
196
|
self.class.loggers.inject(block) do |inner, each_logger|
|
197
197
|
if each_logger.respond_to?(:tagged)
|
198
198
|
tags_for_logger = if each_logger.formatter.current_tags.include?("ActiveJob")
|
199
|
-
|
199
|
+
good_job_tag + tags
|
200
200
|
else
|
201
201
|
tags
|
202
202
|
end
|
data/lib/good_job/notifier.rb
CHANGED
@@ -34,7 +34,7 @@ module GoodJob # :nodoc:
|
|
34
34
|
# @param message [#to_json]
|
35
35
|
def self.notify(message)
|
36
36
|
connection = ActiveRecord::Base.connection
|
37
|
-
connection.exec_query <<~SQL
|
37
|
+
connection.exec_query <<~SQL.squish
|
38
38
|
NOTIFY #{CHANNEL}, #{connection.quote(message.to_json)}
|
39
39
|
SQL
|
40
40
|
end
|
@@ -75,7 +75,7 @@ module GoodJob # :nodoc:
|
|
75
75
|
# If +wait+ is +true+, the notifier will wait for background thread to shutdown.
|
76
76
|
# If +wait+ is +false+, this method will return immediately even though threads may still be running.
|
77
77
|
# Use {#shutdown?} to determine whether threads have stopped.
|
78
|
-
# @param wait [Boolean] Wait for actively executing
|
78
|
+
# @param wait [Boolean] Wait for actively executing threads to finish
|
79
79
|
# @return [void]
|
80
80
|
def shutdown(wait: true)
|
81
81
|
return unless @pool.running?
|
@@ -147,7 +147,7 @@ module GoodJob # :nodoc:
|
|
147
147
|
pg_conn.exec("SET application_name = #{pg_conn.escape_identifier(self.class.name)}")
|
148
148
|
yield pg_conn
|
149
149
|
ensure
|
150
|
-
ar_conn
|
150
|
+
ar_conn&.disconnect!
|
151
151
|
end
|
152
152
|
end
|
153
153
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'concurrent/atomic/atomic_boolean'
|
2
|
+
|
3
|
+
module GoodJob # :nodoc:
|
4
|
+
#
|
5
|
+
# Pollers regularly wake up execution threads to check for new work.
|
6
|
+
#
|
7
|
+
class Poller
|
8
|
+
# Defaults for instance of Concurrent::TimerTask.
|
9
|
+
# The timer controls how and when sleeping threads check for new work.
|
10
|
+
DEFAULT_TIMER_OPTIONS = {
|
11
|
+
execution_interval: Configuration::DEFAULT_POLL_INTERVAL,
|
12
|
+
timeout_interval: 1,
|
13
|
+
run_now: true,
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
# @!attribute [r] instances
|
17
|
+
# @!scope class
|
18
|
+
# List of all instantiated Pollers in the current process.
|
19
|
+
# @return [array<GoodJob:Poller>]
|
20
|
+
cattr_reader :instances, default: [], instance_reader: false
|
21
|
+
|
22
|
+
def self.from_configuration(configuration)
|
23
|
+
GoodJob::Poller.new(poll_interval: configuration.poll_interval)
|
24
|
+
end
|
25
|
+
|
26
|
+
# List of recipients that will receive notifications.
|
27
|
+
# @return [Array<#call, Array(Object, Symbol)>]
|
28
|
+
attr_reader :recipients
|
29
|
+
|
30
|
+
# @param recipients [Array<#call, Array(Object, Symbol)>]
|
31
|
+
# @param poll_interval [Hash] number of seconds between polls
|
32
|
+
def initialize(*recipients, poll_interval: nil)
|
33
|
+
@recipients = Concurrent::Array.new(recipients)
|
34
|
+
|
35
|
+
@timer_options = DEFAULT_TIMER_OPTIONS.dup
|
36
|
+
@timer_options[:execution_interval] = poll_interval if poll_interval.present?
|
37
|
+
|
38
|
+
self.class.instances << self
|
39
|
+
|
40
|
+
create_pool
|
41
|
+
end
|
42
|
+
|
43
|
+
# Shut down the poller.
|
44
|
+
# If +wait+ is +true+, the poller will wait for background thread to shutdown.
|
45
|
+
# If +wait+ is +false+, this method will return immediately even though threads may still be running.
|
46
|
+
# Use {#shutdown?} to determine whether threads have stopped.
|
47
|
+
# @param wait [Boolean] Wait for actively executing threads to finish
|
48
|
+
# @return [void]
|
49
|
+
def shutdown(wait: true)
|
50
|
+
return unless @timer&.running?
|
51
|
+
|
52
|
+
@timer.shutdown
|
53
|
+
@timer.wait_for_termination if wait
|
54
|
+
end
|
55
|
+
|
56
|
+
# Tests whether the poller is shutdown.
|
57
|
+
# @return [true, false, nil]
|
58
|
+
def shutdown?
|
59
|
+
!@timer&.running?
|
60
|
+
end
|
61
|
+
|
62
|
+
# Restart the poller.
|
63
|
+
# When shutdown, start; or shutdown and start.
|
64
|
+
# @param wait [Boolean] Wait for background thread to finish
|
65
|
+
# @return [void]
|
66
|
+
def restart(wait: true)
|
67
|
+
shutdown(wait: wait)
|
68
|
+
create_pool
|
69
|
+
end
|
70
|
+
|
71
|
+
# Invoked on completion of TimerTask task.
|
72
|
+
# @!visibility private
|
73
|
+
# @return [void]
|
74
|
+
def timer_observer(time, executed_task, thread_error)
|
75
|
+
GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
|
76
|
+
instrument("finished_timer_task", { result: executed_task, error: thread_error, time: time })
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def create_pool
|
82
|
+
return if @timer_options[:execution_interval] <= 0
|
83
|
+
|
84
|
+
@timer = Concurrent::TimerTask.new(@timer_options) do
|
85
|
+
recipients.each do |recipient|
|
86
|
+
target, method_name = recipient.is_a?(Array) ? recipient : [recipient, :call]
|
87
|
+
target.send(method_name)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
@timer.add_observer(self, :timer_observer)
|
91
|
+
@timer.execute
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/good_job/scheduler.rb
CHANGED
@@ -26,14 +26,6 @@ module GoodJob # :nodoc:
|
|
26
26
|
fallback_policy: :discard,
|
27
27
|
}.freeze
|
28
28
|
|
29
|
-
# Defaults for instance of Concurrent::TimerTask.
|
30
|
-
# The timer controls how and when sleeping threads check for new work.
|
31
|
-
DEFAULT_TIMER_OPTIONS = {
|
32
|
-
execution_interval: Configuration::DEFAULT_POLL_INTERVAL,
|
33
|
-
timeout_interval: 1,
|
34
|
-
run_now: true,
|
35
|
-
}.freeze
|
36
|
-
|
37
29
|
# @!attribute [r] instances
|
38
30
|
# @!scope class
|
39
31
|
# List of all instantiated Schedulers in the current process.
|
@@ -41,7 +33,6 @@ module GoodJob # :nodoc:
|
|
41
33
|
cattr_reader :instances, default: [], instance_reader: false
|
42
34
|
|
43
35
|
# Creates GoodJob::Scheduler(s) and Performers from a GoodJob::Configuration instance.
|
44
|
-
# TODO: move this to GoodJob::Configuration
|
45
36
|
# @param configuration [GoodJob::Configuration]
|
46
37
|
# @return [GoodJob::Scheduler, GoodJob::MultiScheduler]
|
47
38
|
def self.from_configuration(configuration)
|
@@ -53,7 +44,7 @@ module GoodJob # :nodoc:
|
|
53
44
|
parsed = GoodJob::Job.queue_parser(queue_string)
|
54
45
|
job_filter = proc do |state|
|
55
46
|
if parsed[:exclude]
|
56
|
-
|
47
|
+
parsed[:exclude].exclude?(state[:queue_name])
|
57
48
|
elsif parsed[:include]
|
58
49
|
parsed[:include].include? state[:queue_name]
|
59
50
|
else
|
@@ -62,7 +53,7 @@ module GoodJob # :nodoc:
|
|
62
53
|
end
|
63
54
|
job_performer = GoodJob::Performer.new(job_query, :perform_with_advisory_lock, name: queue_string, filter: job_filter)
|
64
55
|
|
65
|
-
GoodJob::Scheduler.new(job_performer, max_threads: max_threads
|
56
|
+
GoodJob::Scheduler.new(job_performer, max_threads: max_threads)
|
66
57
|
end
|
67
58
|
|
68
59
|
if schedulers.size > 1
|
@@ -73,23 +64,19 @@ module GoodJob # :nodoc:
|
|
73
64
|
end
|
74
65
|
|
75
66
|
# @param performer [GoodJob::Performer]
|
76
|
-
# @param max_threads [Numeric, nil]
|
77
|
-
|
78
|
-
def initialize(performer, max_threads: nil, poll_interval: nil)
|
67
|
+
# @param max_threads [Numeric, nil] number of seconds between polls for jobs
|
68
|
+
def initialize(performer, max_threads: nil)
|
79
69
|
raise ArgumentError, "Performer argument must implement #next" unless performer.respond_to?(:next)
|
80
70
|
|
81
71
|
self.class.instances << self
|
82
72
|
|
83
73
|
@performer = performer
|
84
74
|
|
85
|
-
@timer_options = DEFAULT_TIMER_OPTIONS.dup
|
86
|
-
@timer_options[:execution_interval] = poll_interval if poll_interval.present?
|
87
|
-
|
88
75
|
@pool_options = DEFAULT_POOL_OPTIONS.dup
|
89
76
|
@pool_options[:max_threads] = max_threads if max_threads.present?
|
90
|
-
@pool_options[:name] = "GoodJob::Scheduler(queues=#{@performer.name} max_threads=#{@pool_options[:max_threads]}
|
77
|
+
@pool_options[:name] = "GoodJob::Scheduler(queues=#{@performer.name} max_threads=#{@pool_options[:max_threads]})"
|
91
78
|
|
92
|
-
|
79
|
+
create_pool
|
93
80
|
end
|
94
81
|
|
95
82
|
# Shut down the scheduler.
|
@@ -100,28 +87,20 @@ module GoodJob # :nodoc:
|
|
100
87
|
# @param wait [Boolean] Wait for actively executing jobs to finish
|
101
88
|
# @return [void]
|
102
89
|
def shutdown(wait: true)
|
103
|
-
|
90
|
+
return unless @pool&.running?
|
104
91
|
|
105
92
|
instrument("scheduler_shutdown_start", { wait: wait })
|
106
93
|
instrument("scheduler_shutdown", { wait: wait }) do
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
# TODO: Should be killed if wait is not true
|
111
|
-
end
|
112
|
-
|
113
|
-
if @pool&.running?
|
114
|
-
@pool.shutdown
|
115
|
-
@pool.wait_for_termination if wait
|
116
|
-
# TODO: Should be killed if wait is not true
|
117
|
-
end
|
94
|
+
@pool.shutdown
|
95
|
+
@pool.wait_for_termination if wait
|
96
|
+
# TODO: Should be killed if wait is not true
|
118
97
|
end
|
119
98
|
end
|
120
99
|
|
121
100
|
# Tests whether the scheduler is shutdown.
|
122
101
|
# @return [true, false, nil]
|
123
102
|
def shutdown?
|
124
|
-
|
103
|
+
!@pool&.running?
|
125
104
|
end
|
126
105
|
|
127
106
|
# Restart the Scheduler.
|
@@ -131,8 +110,7 @@ module GoodJob # :nodoc:
|
|
131
110
|
def restart(wait: true)
|
132
111
|
instrument("scheduler_restart_pools") do
|
133
112
|
shutdown(wait: wait) unless shutdown?
|
134
|
-
|
135
|
-
@_shutdown = false
|
113
|
+
create_pool
|
136
114
|
end
|
137
115
|
end
|
138
116
|
|
@@ -157,14 +135,6 @@ module GoodJob # :nodoc:
|
|
157
135
|
true
|
158
136
|
end
|
159
137
|
|
160
|
-
# Invoked on completion of TimerTask task.
|
161
|
-
# @!visibility private
|
162
|
-
# @return [void]
|
163
|
-
def timer_observer(time, executed_task, thread_error)
|
164
|
-
GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
|
165
|
-
instrument("finished_timer_task", { result: executed_task, error: thread_error, time: time })
|
166
|
-
end
|
167
|
-
|
168
138
|
# Invoked on completion of ThreadPoolExecutor task
|
169
139
|
# @!visibility private
|
170
140
|
# @return [void]
|
@@ -176,14 +146,9 @@ module GoodJob # :nodoc:
|
|
176
146
|
|
177
147
|
private
|
178
148
|
|
179
|
-
def
|
180
|
-
instrument("
|
149
|
+
def create_pool
|
150
|
+
instrument("scheduler_create_pool", { performer_name: @performer.name, max_threads: @pool_options[:max_threads] }) do
|
181
151
|
@pool = ThreadPoolExecutor.new(@pool_options)
|
182
|
-
next unless @timer_options[:execution_interval].positive?
|
183
|
-
|
184
|
-
@timer = Concurrent::TimerTask.new(@timer_options) { create_thread }
|
185
|
-
@timer.add_observer(self, :timer_observer)
|
186
|
-
@timer.execute
|
187
152
|
end
|
188
153
|
end
|
189
154
|
|
@@ -196,20 +161,20 @@ module GoodJob # :nodoc:
|
|
196
161
|
|
197
162
|
ActiveSupport::Notifications.instrument("#{name}.good_job", payload, &block)
|
198
163
|
end
|
199
|
-
end
|
200
164
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
165
|
+
# Custom sub-class of +Concurrent::ThreadPoolExecutor+ to add additional worker status.
|
166
|
+
# @private
|
167
|
+
class ThreadPoolExecutor < Concurrent::ThreadPoolExecutor
|
168
|
+
# Number of inactive threads available to execute tasks.
|
169
|
+
# https://github.com/ruby-concurrency/concurrent-ruby/issues/684#issuecomment-427594437
|
170
|
+
# @return [Integer]
|
171
|
+
def ready_worker_count
|
172
|
+
synchronize do
|
173
|
+
workers_still_to_be_created = @max_length - @pool.length
|
174
|
+
workers_created_but_waiting = @ready.length
|
175
|
+
|
176
|
+
workers_still_to_be_created + workers_created_but_waiting
|
177
|
+
end
|
213
178
|
end
|
214
179
|
end
|
215
180
|
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.3.
|
4
|
+
version: 1.3.1
|
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-
|
11
|
+
date: 2020-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|
@@ -290,6 +290,20 @@ dependencies:
|
|
290
290
|
- - ">="
|
291
291
|
- !ruby/object:Gem::Version
|
292
292
|
version: '0'
|
293
|
+
- !ruby/object:Gem::Dependency
|
294
|
+
name: rails
|
295
|
+
requirement: !ruby/object:Gem::Requirement
|
296
|
+
requirements:
|
297
|
+
- - ">="
|
298
|
+
- !ruby/object:Gem::Version
|
299
|
+
version: '0'
|
300
|
+
type: :development
|
301
|
+
prerelease: false
|
302
|
+
version_requirements: !ruby/object:Gem::Requirement
|
303
|
+
requirements:
|
304
|
+
- - ">="
|
305
|
+
- !ruby/object:Gem::Version
|
306
|
+
version: '0'
|
293
307
|
- !ruby/object:Gem::Dependency
|
294
308
|
name: rbtrace
|
295
309
|
requirement: !ruby/object:Gem::Requirement
|
@@ -475,6 +489,7 @@ files:
|
|
475
489
|
- lib/good_job/multi_scheduler.rb
|
476
490
|
- lib/good_job/notifier.rb
|
477
491
|
- lib/good_job/performer.rb
|
492
|
+
- lib/good_job/poller.rb
|
478
493
|
- lib/good_job/railtie.rb
|
479
494
|
- lib/good_job/scheduler.rb
|
480
495
|
- lib/good_job/version.rb
|
@@ -510,7 +525,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
510
525
|
- !ruby/object:Gem::Version
|
511
526
|
version: '0'
|
512
527
|
requirements: []
|
513
|
-
rubygems_version: 3.
|
528
|
+
rubygems_version: 3.1.4
|
514
529
|
signing_key:
|
515
530
|
specification_version: 4
|
516
531
|
summary: A multithreaded, Postgres-based ActiveJob backend for Ruby on Rails
|