good_job 1.3.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|