good_job 1.6.0 → 1.9.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 +95 -9
- data/README.md +27 -17
- data/exe/good_job +3 -1
- data/lib/active_job/queue_adapters/good_job_adapter.rb +0 -4
- data/lib/good_job.rb +43 -7
- data/lib/good_job/adapter.rb +57 -22
- data/lib/good_job/cli.rb +26 -7
- data/lib/good_job/configuration.rb +62 -39
- data/lib/good_job/job.rb +26 -1
- data/lib/good_job/job_performer.rb +11 -0
- data/lib/good_job/lockable.rb +2 -2
- data/lib/good_job/multi_scheduler.rb +12 -7
- data/lib/good_job/notifier.rb +44 -35
- data/lib/good_job/poller.rb +32 -21
- data/lib/good_job/railtie.rb +4 -0
- data/lib/good_job/scheduler.rb +156 -51
- data/lib/good_job/version.rb +1 -1
- metadata +3 -17
data/lib/good_job/cli.rb
CHANGED
@@ -15,9 +15,21 @@ module GoodJob
|
|
15
15
|
# Requiring this loads the application's configuration and classes.
|
16
16
|
RAILS_ENVIRONMENT_RB = File.expand_path("config/environment.rb")
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
class << self
|
19
|
+
# Whether the CLI is running from the executable
|
20
|
+
# @return [Boolean, nil]
|
21
|
+
attr_accessor :within_exe
|
22
|
+
alias within_exe? within_exe
|
23
|
+
|
24
|
+
# Whether to log to STDOUT
|
25
|
+
# @return [Boolean, nil]
|
26
|
+
attr_accessor :log_to_stdout
|
27
|
+
alias log_to_stdout? log_to_stdout
|
28
|
+
|
29
|
+
# @!visibility private
|
30
|
+
def exit_on_failure?
|
31
|
+
true
|
32
|
+
end
|
21
33
|
end
|
22
34
|
|
23
35
|
# @!macro thor.desc
|
@@ -49,6 +61,14 @@ module GoodJob
|
|
49
61
|
type: :numeric,
|
50
62
|
banner: 'SECONDS',
|
51
63
|
desc: "Interval between polls for available jobs in seconds (env var: GOOD_JOB_POLL_INTERVAL, default: 5)"
|
64
|
+
method_option :max_cache,
|
65
|
+
type: :numeric,
|
66
|
+
banner: 'COUNT',
|
67
|
+
desc: "Maximum number of scheduled jobs to cache in memory (env var: GOOD_JOB_MAX_CACHE, default: 10000)"
|
68
|
+
method_option :shutdown_timeout,
|
69
|
+
type: :numeric,
|
70
|
+
banner: 'SECONDS',
|
71
|
+
desc: "Number of seconds to wait for jobs to finish when shutting down before stopping the thread. (env var: GOOD_JOB_SHUTDOWN_TIMEOUT, default: -1 (forever))"
|
52
72
|
method_option :daemonize,
|
53
73
|
type: :boolean,
|
54
74
|
desc: "Run as a background daemon (default: false)"
|
@@ -77,9 +97,8 @@ module GoodJob
|
|
77
97
|
break if @stop_good_job_executable || scheduler.shutdown? || notifier.shutdown?
|
78
98
|
end
|
79
99
|
|
80
|
-
notifier
|
81
|
-
|
82
|
-
scheduler.shutdown
|
100
|
+
executors = [notifier, poller, scheduler]
|
101
|
+
GoodJob._shutdown_all(executors, timeout: configuration.shutdown_timeout)
|
83
102
|
end
|
84
103
|
|
85
104
|
default_task :start
|
@@ -129,7 +148,7 @@ module GoodJob
|
|
129
148
|
# Rails or from the application can be set up here.
|
130
149
|
def set_up_application!
|
131
150
|
require RAILS_ENVIRONMENT_RB
|
132
|
-
return unless
|
151
|
+
return unless GoodJob::CLI.log_to_stdout? && !ActiveSupport::Logger.logger_outputs_to?(GoodJob.logger, $stdout)
|
133
152
|
|
134
153
|
GoodJob::LogSubscriber.loggers << ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new($stdout))
|
135
154
|
GoodJob::LogSubscriber.reset_logger
|
@@ -5,12 +5,18 @@ module GoodJob
|
|
5
5
|
# set options to get the final values for each option.
|
6
6
|
#
|
7
7
|
class Configuration
|
8
|
+
# Valid execution modes.
|
9
|
+
EXECUTION_MODES = [:async, :async_server, :external, :inline].freeze
|
8
10
|
# Default number of threads to use per {Scheduler}
|
9
11
|
DEFAULT_MAX_THREADS = 5
|
10
12
|
# Default number of seconds between polls for jobs
|
11
|
-
DEFAULT_POLL_INTERVAL =
|
13
|
+
DEFAULT_POLL_INTERVAL = 10
|
14
|
+
# Default number of threads to use per {Scheduler}
|
15
|
+
DEFAULT_MAX_CACHE = 10000
|
12
16
|
# Default number of seconds to preserve jobs for {CLI#cleanup_preserved_jobs}
|
13
17
|
DEFAULT_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO = 24 * 60 * 60
|
18
|
+
# Default to always wait for jobs to finish for {Adapter#shutdown}
|
19
|
+
DEFAULT_SHUTDOWN_TIMEOUT = -1
|
14
20
|
|
15
21
|
# The options that were explicitly set when initializing +Configuration+.
|
16
22
|
# @return [Hash]
|
@@ -31,38 +37,30 @@ module GoodJob
|
|
31
37
|
@env = env
|
32
38
|
end
|
33
39
|
|
40
|
+
def validate!
|
41
|
+
raise ArgumentError, "GoodJob execution mode must be one of #{EXECUTION_MODES.join(', ')}. It was '#{execution_mode}' which is not valid." unless execution_mode.in?(EXECUTION_MODES)
|
42
|
+
end
|
43
|
+
|
34
44
|
# Specifies how and where jobs should be executed. See {Adapter#initialize}
|
35
45
|
# for more details on possible values.
|
36
|
-
#
|
37
|
-
# When running inside a Rails app, you may want to use
|
38
|
-
# {#rails_execution_mode}, which takes the current Rails environment into
|
39
|
-
# account when determining the final value.
|
40
|
-
#
|
41
|
-
# @param default [Symbol]
|
42
|
-
# Value to use if none was specified in the configuration.
|
43
46
|
# @return [Symbol]
|
44
|
-
def execution_mode
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
54
|
-
end
|
47
|
+
def execution_mode
|
48
|
+
@_execution_mode ||= begin
|
49
|
+
mode = if GoodJob::CLI.within_exe?
|
50
|
+
:external
|
51
|
+
else
|
52
|
+
options[:execution_mode] ||
|
53
|
+
rails_config[:execution_mode] ||
|
54
|
+
env['GOOD_JOB_EXECUTION_MODE']
|
55
|
+
end
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
:inline
|
64
|
-
else
|
65
|
-
:external
|
57
|
+
if mode
|
58
|
+
mode.to_sym
|
59
|
+
elsif Rails.env.development? || Rails.env.test?
|
60
|
+
:inline
|
61
|
+
else
|
62
|
+
:external
|
63
|
+
end
|
66
64
|
end
|
67
65
|
end
|
68
66
|
|
@@ -73,10 +71,10 @@ module GoodJob
|
|
73
71
|
def max_threads
|
74
72
|
(
|
75
73
|
options[:max_threads] ||
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
74
|
+
rails_config[:max_threads] ||
|
75
|
+
env['GOOD_JOB_MAX_THREADS'] ||
|
76
|
+
env['RAILS_MAX_THREADS'] ||
|
77
|
+
DEFAULT_MAX_THREADS
|
80
78
|
).to_i
|
81
79
|
end
|
82
80
|
|
@@ -99,21 +97,46 @@ module GoodJob
|
|
99
97
|
def poll_interval
|
100
98
|
(
|
101
99
|
options[:poll_interval] ||
|
102
|
-
|
103
|
-
|
104
|
-
|
100
|
+
rails_config[:poll_interval] ||
|
101
|
+
env['GOOD_JOB_POLL_INTERVAL'] ||
|
102
|
+
DEFAULT_POLL_INTERVAL
|
103
|
+
).to_i
|
104
|
+
end
|
105
|
+
|
106
|
+
# The maximum number of future-scheduled jobs to store in memory.
|
107
|
+
# Storing future-scheduled jobs in memory reduces execution latency
|
108
|
+
# at the cost of increased memory usage. 10,000 stored jobs = ~20MB.
|
109
|
+
# @return [Integer]
|
110
|
+
def max_cache
|
111
|
+
(
|
112
|
+
options[:max_cache] ||
|
113
|
+
rails_config[:max_cache] ||
|
114
|
+
env['GOOD_JOB_MAX_CACHE'] ||
|
115
|
+
DEFAULT_MAX_CACHE
|
105
116
|
).to_i
|
106
117
|
end
|
107
118
|
|
119
|
+
# The number of seconds to wait for jobs to finish when shutting down
|
120
|
+
# before stopping the thread. +-1+ is forever.
|
121
|
+
# @return [Numeric]
|
122
|
+
def shutdown_timeout
|
123
|
+
(
|
124
|
+
options[:shutdown_timeout] ||
|
125
|
+
rails_config[:shutdown_timeout] ||
|
126
|
+
env['GOOD_JOB_SHUTDOWN_TIMEOUT'] ||
|
127
|
+
DEFAULT_SHUTDOWN_TIMEOUT
|
128
|
+
).to_f
|
129
|
+
end
|
130
|
+
|
108
131
|
# Number of seconds to preserve jobs when using the +good_job cleanup_preserved_jobs+ CLI command.
|
109
132
|
# This configuration is only used when {GoodJob.preserve_job_records} is +true+.
|
110
133
|
# @return [Integer]
|
111
134
|
def cleanup_preserved_jobs_before_seconds_ago
|
112
135
|
(
|
113
136
|
options[:before_seconds_ago] ||
|
114
|
-
|
115
|
-
|
116
|
-
|
137
|
+
rails_config[:cleanup_preserved_jobs_before_seconds_ago] ||
|
138
|
+
env['GOOD_JOB_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO'] ||
|
139
|
+
DEFAULT_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO
|
117
140
|
).to_i
|
118
141
|
end
|
119
142
|
|
data/lib/good_job/job.rb
CHANGED
@@ -2,7 +2,7 @@ module GoodJob
|
|
2
2
|
#
|
3
3
|
# Represents a request to perform an +ActiveJob+ job.
|
4
4
|
#
|
5
|
-
class Job <
|
5
|
+
class Job < Object.const_get(GoodJob.active_record_parent_class)
|
6
6
|
include Lockable
|
7
7
|
|
8
8
|
# Raised if something attempts to execute a previously completed Job again.
|
@@ -15,6 +15,8 @@ module GoodJob
|
|
15
15
|
|
16
16
|
self.table_name = 'good_jobs'.freeze
|
17
17
|
|
18
|
+
attr_readonly :serialized_params
|
19
|
+
|
18
20
|
# Parse a string representing a group of queues into a more readable data
|
19
21
|
# structure.
|
20
22
|
# @return [Hash]
|
@@ -72,6 +74,12 @@ module GoodJob
|
|
72
74
|
# @return [ActiveRecord::Relation]
|
73
75
|
scope :priority_ordered, -> { order('priority DESC NULLS LAST') }
|
74
76
|
|
77
|
+
# Order jobs by scheduled (unscheduled or soonest first).
|
78
|
+
# @!method schedule_ordered
|
79
|
+
# @!scope class
|
80
|
+
# @return [ActiveRecord::Relation]
|
81
|
+
scope :schedule_ordered, -> { order(Arel.sql('COALESCE(scheduled_at, created_at) ASC')) }
|
82
|
+
|
75
83
|
# Get Jobs were completed before the given timestamp. If no timestamp is
|
76
84
|
# provided, get all jobs that have been completed. By default, GoodJob
|
77
85
|
# deletes jobs after they are completed and this will find no jobs.
|
@@ -147,6 +155,23 @@ module GoodJob
|
|
147
155
|
[good_job, result, error] if good_job
|
148
156
|
end
|
149
157
|
|
158
|
+
# Fetches the scheduled execution time of the next eligible Job(s).
|
159
|
+
# @return [Array<(DateTime)>]
|
160
|
+
def self.next_scheduled_at(after: nil, limit: 100, now_limit: nil)
|
161
|
+
query = advisory_unlocked.unfinished.schedule_ordered
|
162
|
+
|
163
|
+
after ||= Time.current
|
164
|
+
after_query = query.where('scheduled_at > ?', after).or query.where(scheduled_at: nil).where('created_at > ?', after)
|
165
|
+
after_at = after_query.limit(limit).pluck(:scheduled_at, :created_at).map { |timestamps| timestamps.compact.first }
|
166
|
+
|
167
|
+
if now_limit&.positive?
|
168
|
+
now_query = query.where('scheduled_at < ?', Time.current).or query.where(scheduled_at: nil)
|
169
|
+
now_at = now_query.limit(now_limit).pluck(:scheduled_at, :created_at).map { |timestamps| timestamps.compact.first }
|
170
|
+
end
|
171
|
+
|
172
|
+
Array(now_at) + after_at
|
173
|
+
end
|
174
|
+
|
150
175
|
# Places an ActiveJob job on a queue by creating a new {Job} record.
|
151
176
|
# @param active_job [ActiveJob::Base]
|
152
177
|
# The job to enqueue.
|
@@ -39,6 +39,8 @@ module GoodJob
|
|
39
39
|
# @return [Boolean] whether the performer's {#next} method should be
|
40
40
|
# called in the current state.
|
41
41
|
def next?(state = {})
|
42
|
+
return true unless state[:queue_name]
|
43
|
+
|
42
44
|
if parsed_queues[:exclude]
|
43
45
|
parsed_queues[:exclude].exclude?(state[:queue_name])
|
44
46
|
elsif parsed_queues[:include]
|
@@ -48,6 +50,15 @@ module GoodJob
|
|
48
50
|
end
|
49
51
|
end
|
50
52
|
|
53
|
+
# The Returns timestamps of when next tasks may be available.
|
54
|
+
# @param after [DateTime, Time, nil] future jobs scheduled after this time
|
55
|
+
# @param limit [Integer] number of future timestamps to return
|
56
|
+
# @param now_limit [Integer] number of past timestamps to return
|
57
|
+
# @return [Array<(Time, DateTime)>, nil]
|
58
|
+
def next_at(after: nil, limit: nil, now_limit: nil)
|
59
|
+
job_query.next_scheduled_at(after: after, limit: limit, now_limit: now_limit)
|
60
|
+
end
|
61
|
+
|
51
62
|
private
|
52
63
|
|
53
64
|
attr_reader :queue_string
|
data/lib/good_job/lockable.rb
CHANGED
@@ -143,7 +143,7 @@ module GoodJob
|
|
143
143
|
def supports_cte_materialization_specifiers?
|
144
144
|
return @_supports_cte_materialization_specifiers if defined?(@_supports_cte_materialization_specifiers)
|
145
145
|
|
146
|
-
@_supports_cte_materialization_specifiers =
|
146
|
+
@_supports_cte_materialization_specifiers = connection.postgresql_version >= 120000
|
147
147
|
end
|
148
148
|
end
|
149
149
|
|
@@ -158,7 +158,7 @@ module GoodJob
|
|
158
158
|
WHERE pg_try_advisory_lock(('x'||substr(md5($1 || $2::text), 1, 16))::bit(64)::bigint)
|
159
159
|
SQL
|
160
160
|
binds = [[nil, self.class.table_name], [nil, send(self.class.primary_key)]]
|
161
|
-
|
161
|
+
self.class.connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Lock', binds).any?
|
162
162
|
end
|
163
163
|
|
164
164
|
# Releases an advisory lock on this record if it is locked by this database
|
@@ -1,16 +1,16 @@
|
|
1
1
|
module GoodJob
|
2
2
|
# Delegates the interface of a single {Scheduler} to multiple Schedulers.
|
3
3
|
class MultiScheduler
|
4
|
-
# @return [
|
4
|
+
# @return [Array<Scheduler>] List of the scheduler delegates
|
5
5
|
attr_reader :schedulers
|
6
6
|
|
7
7
|
def initialize(schedulers)
|
8
8
|
@schedulers = schedulers
|
9
9
|
end
|
10
10
|
|
11
|
-
# Delegates to {Scheduler#
|
12
|
-
def
|
13
|
-
schedulers.
|
11
|
+
# Delegates to {Scheduler#running?}.
|
12
|
+
def running?
|
13
|
+
schedulers.all?(&:running?)
|
14
14
|
end
|
15
15
|
|
16
16
|
# Delegates to {Scheduler#shutdown?}.
|
@@ -18,9 +18,14 @@ module GoodJob
|
|
18
18
|
schedulers.all?(&:shutdown?)
|
19
19
|
end
|
20
20
|
|
21
|
+
# Delegates to {Scheduler#shutdown}.
|
22
|
+
def shutdown(timeout: -1)
|
23
|
+
GoodJob._shutdown_all(schedulers, timeout: timeout)
|
24
|
+
end
|
25
|
+
|
21
26
|
# Delegates to {Scheduler#restart}.
|
22
|
-
def restart(
|
23
|
-
schedulers
|
27
|
+
def restart(timeout: -1)
|
28
|
+
GoodJob._shutdown_all(schedulers, :restart, timeout: timeout)
|
24
29
|
end
|
25
30
|
|
26
31
|
# Delegates to {Scheduler#create_thread}.
|
@@ -39,7 +44,7 @@ module GoodJob
|
|
39
44
|
|
40
45
|
if results.any?
|
41
46
|
true
|
42
|
-
elsif results.any?
|
47
|
+
elsif results.any?(false)
|
43
48
|
false
|
44
49
|
else # rubocop:disable Style/EmptyElse
|
45
50
|
nil
|
data/lib/good_job/notifier.rb
CHANGED
@@ -15,7 +15,7 @@ module GoodJob # :nodoc:
|
|
15
15
|
# Default Postgres channel for LISTEN/NOTIFY
|
16
16
|
CHANNEL = 'good_job'.freeze
|
17
17
|
# Defaults for instance of Concurrent::ThreadPoolExecutor
|
18
|
-
|
18
|
+
EXECUTOR_OPTIONS = {
|
19
19
|
name: name,
|
20
20
|
min_threads: 0,
|
21
21
|
max_threads: 1,
|
@@ -30,13 +30,13 @@ module GoodJob # :nodoc:
|
|
30
30
|
# @!attribute [r] instances
|
31
31
|
# @!scope class
|
32
32
|
# List of all instantiated Notifiers in the current process.
|
33
|
-
# @return [
|
33
|
+
# @return [Array<GoodJob::Adapter>]
|
34
34
|
cattr_reader :instances, default: [], instance_reader: false
|
35
35
|
|
36
36
|
# Send a message via Postgres NOTIFY
|
37
37
|
# @param message [#to_json]
|
38
38
|
def self.notify(message)
|
39
|
-
connection =
|
39
|
+
connection = Job.connection
|
40
40
|
connection.exec_query <<~SQL.squish
|
41
41
|
NOTIFY #{CHANNEL}, #{connection.quote(message.to_json)}
|
42
42
|
SQL
|
@@ -53,7 +53,7 @@ module GoodJob # :nodoc:
|
|
53
53
|
|
54
54
|
self.class.instances << self
|
55
55
|
|
56
|
-
|
56
|
+
create_executor
|
57
57
|
listen
|
58
58
|
end
|
59
59
|
|
@@ -63,34 +63,43 @@ module GoodJob # :nodoc:
|
|
63
63
|
@listening.true?
|
64
64
|
end
|
65
65
|
|
66
|
-
#
|
67
|
-
#
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
listen
|
74
|
-
end
|
66
|
+
# Tests whether the notifier is running.
|
67
|
+
# @return [true, false, nil]
|
68
|
+
delegate :running?, to: :executor, allow_nil: true
|
69
|
+
|
70
|
+
# Tests whether the scheduler is shutdown.
|
71
|
+
# @return [true, false, nil]
|
72
|
+
delegate :shutdown?, to: :executor, allow_nil: true
|
75
73
|
|
76
74
|
# Shut down the notifier.
|
77
75
|
# This stops the background LISTENing thread.
|
78
|
-
# If +wait+ is +true+, the notifier will wait for background thread to shutdown.
|
79
|
-
# If +wait+ is +false+, this method will return immediately even though threads may still be running.
|
80
76
|
# Use {#shutdown?} to determine whether threads have stopped.
|
81
|
-
# @param
|
77
|
+
# @param timeout [nil, Numeric] Seconds to wait for active threads.
|
78
|
+
#
|
79
|
+
# * +nil+, the scheduler will trigger a shutdown but not wait for it to complete.
|
80
|
+
# * +-1+, the scheduler will wait until the shutdown is complete.
|
81
|
+
# * +0+, the scheduler will immediately shutdown and stop any threads.
|
82
|
+
# * A positive number will wait that many seconds before stopping any remaining active threads.
|
82
83
|
# @return [void]
|
83
|
-
def shutdown(
|
84
|
-
return
|
84
|
+
def shutdown(timeout: -1)
|
85
|
+
return if executor.nil? || executor.shutdown?
|
85
86
|
|
86
|
-
|
87
|
-
|
87
|
+
executor.shutdown if executor.running?
|
88
|
+
|
89
|
+
if executor.shuttingdown? && timeout # rubocop:disable Style/GuardClause
|
90
|
+
executor_wait = timeout.negative? ? nil : timeout
|
91
|
+
executor.kill unless executor.wait_for_termination(executor_wait)
|
92
|
+
end
|
88
93
|
end
|
89
94
|
|
90
|
-
#
|
91
|
-
#
|
92
|
-
|
93
|
-
|
95
|
+
# Restart the notifier.
|
96
|
+
# When shutdown, start; or shutdown and start.
|
97
|
+
# @param timeout [nil, Numeric] Seconds to wait; shares same values as {#shutdown}.
|
98
|
+
# @return [void]
|
99
|
+
def restart(timeout: -1)
|
100
|
+
shutdown(timeout: timeout) if running?
|
101
|
+
create_executor
|
102
|
+
listen
|
94
103
|
end
|
95
104
|
|
96
105
|
# Invoked on completion of ThreadPoolExecutor task
|
@@ -109,36 +118,36 @@ module GoodJob # :nodoc:
|
|
109
118
|
|
110
119
|
private
|
111
120
|
|
112
|
-
|
113
|
-
|
121
|
+
attr_reader :executor
|
122
|
+
|
123
|
+
def create_executor
|
124
|
+
@executor = Concurrent::ThreadPoolExecutor.new(EXECUTOR_OPTIONS)
|
114
125
|
end
|
115
126
|
|
116
127
|
def listen
|
117
|
-
future = Concurrent::Future.new(args: [@recipients,
|
128
|
+
future = Concurrent::Future.new(args: [@recipients, executor, @listening], executor: @executor) do |thr_recipients, thr_executor, thr_listening|
|
118
129
|
with_listen_connection do |conn|
|
119
130
|
ActiveSupport::Notifications.instrument("notifier_listen.good_job") do
|
120
131
|
conn.async_exec("LISTEN #{CHANNEL}").clear
|
121
132
|
end
|
122
133
|
|
123
134
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
124
|
-
|
125
|
-
|
135
|
+
thr_listening.make_true
|
136
|
+
while thr_executor.running?
|
126
137
|
conn.wait_for_notify(WAIT_INTERVAL) do |channel, _pid, payload|
|
127
|
-
listening.make_false
|
128
138
|
next unless channel == CHANNEL
|
129
139
|
|
130
140
|
ActiveSupport::Notifications.instrument("notifier_notified.good_job", { payload: payload })
|
131
141
|
parsed_payload = JSON.parse(payload, symbolize_names: true)
|
132
|
-
|
142
|
+
thr_recipients.each do |recipient|
|
133
143
|
target, method_name = recipient.is_a?(Array) ? recipient : [recipient, :call]
|
134
144
|
target.send(method_name, parsed_payload)
|
135
145
|
end
|
136
146
|
end
|
137
|
-
listening.make_false
|
138
147
|
end
|
139
148
|
end
|
140
149
|
ensure
|
141
|
-
|
150
|
+
thr_listening.make_false
|
142
151
|
ActiveSupport::Notifications.instrument("notifier_unlisten.good_job") do
|
143
152
|
conn.async_exec("UNLISTEN *").clear
|
144
153
|
end
|
@@ -150,8 +159,8 @@ module GoodJob # :nodoc:
|
|
150
159
|
end
|
151
160
|
|
152
161
|
def with_listen_connection
|
153
|
-
ar_conn =
|
154
|
-
|
162
|
+
ar_conn = Job.connection_pool.checkout.tap do |conn|
|
163
|
+
Job.connection_pool.remove(conn)
|
155
164
|
end
|
156
165
|
pg_conn = ar_conn.raw_connection
|
157
166
|
raise AdapterCannotListenError unless pg_conn.respond_to? :wait_for_notify
|