good_job 3.10.1 → 3.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/README.md +2 -0
- data/lib/good_job/active_job_extensions/notify_options.rb +47 -0
- data/lib/good_job/adapter.rb +23 -10
- data/lib/good_job/cli.rb +1 -1
- data/lib/good_job/configuration.rb +10 -0
- data/lib/good_job/notifier.rb +26 -11
- data/lib/good_job/version.rb +1 -1
- data/lib/good_job.rb +2 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0298665dd1e0d426def8cd08d422e41bfaf308f26de80e7079f80f4ae3403259'
|
4
|
+
data.tar.gz: 6ed8d5a5891515f4ef6404c1776a9147df1625d5be1a654b0407eb94cc96f36e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c0d4b598ee46722d8a23c6e1d044ba2a6ab9933998b096a1749896bc4fba7b50f19ec71ce02fc5924732210a0ea0a442a79ff66e34c12b94c3b7e79048f1cae9
|
7
|
+
data.tar.gz: 39fee537236f2304305dc9b5ae794a6045d5fb66450f67e40a859b4f62c05c88dcea2cb85b328839fff94989f260a40a7bc4a76fffffd1107a861bbe7ac19a2b
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v3.11.0](https://github.com/bensheldon/good_job/tree/v3.11.0) (2023-02-06)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.10.1...v3.11.0)
|
6
|
+
|
7
|
+
**Implemented enhancements:**
|
8
|
+
|
9
|
+
- Create ActiveJob extension to disable sending a NOTIFY on enqueue and retry [\#814](https://github.com/bensheldon/good_job/pull/814) ([bensheldon](https://github.com/bensheldon))
|
10
|
+
- Add global enable\_listen\_notify configuration to disable both notify and listen [\#810](https://github.com/bensheldon/good_job/pull/810) ([mitchellhenke](https://github.com/mitchellhenke))
|
11
|
+
|
12
|
+
**Merged pull requests:**
|
13
|
+
|
14
|
+
- Test Matrix: Use Ruby 3.2 to test against all Postgres versions; add PG 15, remove PG 10 [\#828](https://github.com/bensheldon/good_job/pull/828) ([bensheldon](https://github.com/bensheldon))
|
15
|
+
- Bump nokogiri from 1.14.0 to 1.14.1 [\#827](https://github.com/bensheldon/good_job/pull/827) ([dependabot[bot]](https://github.com/apps/dependabot))
|
16
|
+
- Bump rubocop-performance from 1.15.2 to 1.16.0 [\#826](https://github.com/bensheldon/good_job/pull/826) ([dependabot[bot]](https://github.com/apps/dependabot))
|
17
|
+
|
3
18
|
## [v3.10.1](https://github.com/bensheldon/good_job/tree/v3.10.1) (2023-02-06)
|
4
19
|
|
5
20
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.10.0...v3.10.1)
|
data/README.md
CHANGED
@@ -176,6 +176,7 @@ Options:
|
|
176
176
|
[--max-cache=COUNT] # Maximum number of scheduled jobs to cache in memory (env var: GOOD_JOB_MAX_CACHE, default: 10000)
|
177
177
|
[--shutdown-timeout=SECONDS] # 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))
|
178
178
|
[--enable-cron] # Whether to run cron process (default: false)
|
179
|
+
[--enable-listen-notify] # Whether to enqueue and read jobs with Postgres LISTEN/NOTIFY (default: true)
|
179
180
|
[--daemonize] # Run as a background daemon (default: false)
|
180
181
|
[--pidfile=PIDFILE] # Path to write daemonized Process ID (env var: GOOD_JOB_PIDFILE, default: tmp/pids/good_job.pid)
|
181
182
|
[--probe-port=PORT] # Port for http health check (env var: GOOD_JOB_PROBE_PORT, default: nil)
|
@@ -272,6 +273,7 @@ Available configuration options are:
|
|
272
273
|
- `max_cache` (integer) sets the maximum number of scheduled jobs that will be stored in memory to reduce execution latency when also polling for scheduled jobs. Caching 10,000 scheduled jobs uses approximately 20MB of memory. You can also set this with the environment variable `GOOD_JOB_MAX_CACHE`.
|
273
274
|
- `shutdown_timeout` (integer) number of seconds to wait for jobs to finish when shutting down before stopping the thread. Defaults to forever: `-1`. You can also set this with the environment variable `GOOD_JOB_SHUTDOWN_TIMEOUT`.
|
274
275
|
- `enable_cron` (boolean) whether to run cron process. Defaults to `false`. You can also set this with the environment variable `GOOD_JOB_ENABLE_CRON`.
|
276
|
+
- `enable_listen_notify` (boolean) whether to enqueue and read jobs with Postgres LISTEN/NOTIFY. Defaults to `true`. You can also set this with the environment variable `GOOD_JOB_ENABLE_LISTEN_NOTIFY`.
|
275
277
|
- `cron` (hash) cron configuration. Defaults to `{}`. You can also set this as a JSON string with the environment variable `GOOD_JOB_CRON`
|
276
278
|
- `cleanup_discarded_jobs` (boolean) whether to destroy discarded jobs when cleaning up preserved jobs using the `$ good_job cleanup_preserved_jobs` CLI command or calling `GoodJob.cleanup_preserved_jobs`. Defaults to `true`. Can also be set with the environment variable `GOOD_JOB_CLEANUP_DISCARDED_JOBS`. _This configuration is only used when {GoodJob.preserve_job_records} is `true`._
|
277
279
|
- `cleanup_preserved_jobs_before_seconds_ago` (integer) number of seconds to preserve jobs when using the `$ good_job cleanup_preserved_jobs` CLI command or calling `GoodJob.cleanup_preserved_jobs`. Defaults to `1209600` (14 days). Can also be set with the environment variable `GOOD_JOB_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO`. _This configuration is only used when {GoodJob.preserve_job_records} is `true`._
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GoodJob
|
3
|
+
module ActiveJobExtensions
|
4
|
+
# Allows configuring whether GoodJob should emit a NOTIFY event when a job is enqueued.
|
5
|
+
# Configuration will apply either globally to the Job Class, or individually to jobs
|
6
|
+
# on initial enqueue and subsequent retries.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # Include the concern to your job class:
|
10
|
+
# class MyJob < ApplicationJob
|
11
|
+
# include GoodJob::ActiveJobExtensions::Notify
|
12
|
+
# self.good_job_notify = false
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# # Or, configure jobs individually to not notify:
|
16
|
+
# MyJob.set(good_job_notify: false).perform_later
|
17
|
+
#
|
18
|
+
module NotifyOptions
|
19
|
+
extend ActiveSupport::Concern
|
20
|
+
|
21
|
+
module Prepends
|
22
|
+
def enqueue(options = {})
|
23
|
+
self.good_job_notify = options[:good_job_notify] if options.key?(:good_job_notify)
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
def serialize
|
28
|
+
super.tap do |job_data|
|
29
|
+
# Only serialize the value if present to reduce the size of the serialized job
|
30
|
+
job_data["good_job_notify"] = good_job_notify unless good_job_notify.nil?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def deserialize(job_data)
|
35
|
+
super
|
36
|
+
self.good_job_notify = job_data["good_job_notify"]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
included do
|
41
|
+
prepend Prepends
|
42
|
+
class_attribute :good_job_notify, instance_accessor: false, instance_predicate: false, default: nil
|
43
|
+
attr_accessor :good_job_notify
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/good_job/adapter.rb
CHANGED
@@ -90,14 +90,20 @@ module GoodJob
|
|
90
90
|
inline_executions.each(&:advisory_unlock)
|
91
91
|
end
|
92
92
|
|
93
|
-
executions.reject(&:finished_at)
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
93
|
+
non_inline_executions = executions.reject(&:finished_at)
|
94
|
+
if non_inline_executions.any?
|
95
|
+
job_id_to_active_jobs = active_jobs.index_by(&:job_id)
|
96
|
+
non_inline_executions.group_by(&:queue_name).each do |queue_name, executions_by_queue|
|
97
|
+
executions_by_queue.group_by(&:scheduled_at).each do |scheduled_at, executions_by_queue_and_scheduled_at|
|
98
|
+
state = { queue_name: queue_name, count: executions_by_queue_and_scheduled_at.size }
|
99
|
+
state[:scheduled_at] = scheduled_at if scheduled_at
|
100
|
+
|
101
|
+
executed_locally = execute_async? && @scheduler&.create_thread(state)
|
102
|
+
unless executed_locally
|
103
|
+
state[:count] = job_id_to_active_jobs.values_at(*executions_by_queue_and_scheduled_at.map(&:active_job_id)).count { |active_job| send_notify?(active_job) }
|
104
|
+
Notifier.notify(state) unless state[:count].zero?
|
105
|
+
end
|
106
|
+
end
|
101
107
|
end
|
102
108
|
end
|
103
109
|
|
@@ -136,7 +142,7 @@ module GoodJob
|
|
136
142
|
job_state[:scheduled_at] = execution.scheduled_at if execution.scheduled_at
|
137
143
|
|
138
144
|
executed_locally = execute_async? && @scheduler&.create_thread(job_state)
|
139
|
-
Notifier.notify(job_state)
|
145
|
+
Notifier.notify(job_state) if !executed_locally && send_notify?(active_job)
|
140
146
|
end
|
141
147
|
|
142
148
|
execution
|
@@ -193,7 +199,7 @@ module GoodJob
|
|
193
199
|
def start_async
|
194
200
|
return unless execute_async?
|
195
201
|
|
196
|
-
@notifier = GoodJob::Notifier.new
|
202
|
+
@notifier = GoodJob::Notifier.new(enable_listening: GoodJob.configuration.enable_listen_notify)
|
197
203
|
@poller = GoodJob::Poller.new(poll_interval: GoodJob.configuration.poll_interval)
|
198
204
|
@scheduler = GoodJob::Scheduler.from_configuration(GoodJob.configuration, warm_cache_on_initialize: true)
|
199
205
|
@notifier.recipients << [@scheduler, :create_thread]
|
@@ -226,5 +232,12 @@ module GoodJob
|
|
226
232
|
(Concurrent.on_jruby? && self_caller.grep(%r{jruby/rack/rails_booter}).any?) # EXAMPLE: uri:classloader:/jruby/rack/rails_booter.rb:83:in `load_environment'
|
227
233
|
end
|
228
234
|
end
|
235
|
+
|
236
|
+
def send_notify?(active_job)
|
237
|
+
return true unless active_job.respond_to?(:good_job_notify)
|
238
|
+
return false unless GoodJob.configuration.enable_listen_notify
|
239
|
+
|
240
|
+
!(active_job.good_job_notify == false || (active_job.class.good_job_notify == false && active_job.good_job_notify.nil?))
|
241
|
+
end
|
229
242
|
end
|
230
243
|
end
|
data/lib/good_job/cli.rb
CHANGED
@@ -95,7 +95,7 @@ module GoodJob
|
|
95
95
|
|
96
96
|
Daemon.new(pidfile: configuration.pidfile).daemonize if configuration.daemonize?
|
97
97
|
|
98
|
-
notifier = GoodJob::Notifier.new
|
98
|
+
notifier = GoodJob::Notifier.new(enable_listening: GoodJob.configuration.enable_listen_notify)
|
99
99
|
poller = GoodJob::Poller.new(poll_interval: configuration.poll_interval)
|
100
100
|
scheduler = GoodJob::Scheduler.from_configuration(configuration, warm_cache_on_initialize: true)
|
101
101
|
notifier.recipients << [scheduler, :create_thread]
|
@@ -26,6 +26,8 @@ module GoodJob
|
|
26
26
|
DEFAULT_SHUTDOWN_TIMEOUT = -1
|
27
27
|
# Default to not running cron
|
28
28
|
DEFAULT_ENABLE_CRON = false
|
29
|
+
# Default to enabling LISTEN/NOTIFY
|
30
|
+
DEFAULT_ENABLE_LISTEN_NOTIFY = true
|
29
31
|
|
30
32
|
def self.validate_execution_mode(execution_mode)
|
31
33
|
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)
|
@@ -331,6 +333,14 @@ module GoodJob
|
|
331
333
|
env['GOOD_JOB_PROBE_PORT']
|
332
334
|
end
|
333
335
|
|
336
|
+
def enable_listen_notify
|
337
|
+
return options[:enable_listen_notify] unless options[:enable_listen_notify].nil?
|
338
|
+
return rails_config[:enable_listen_notify] unless rails_config[:enable_listen_notify].nil?
|
339
|
+
return ActiveModel::Type::Boolean.new.cast(env['GOOD_JOB_ENABLE_LISTEN_NOTIFY']) unless env['GOOD_JOB_ENABLE_LISTEN_NOTIFY'].nil?
|
340
|
+
|
341
|
+
DEFAULT_ENABLE_LISTEN_NOTIFY
|
342
|
+
end
|
343
|
+
|
334
344
|
private
|
335
345
|
|
336
346
|
def rails_config
|
data/lib/good_job/notifier.rb
CHANGED
@@ -68,11 +68,14 @@ module GoodJob # :nodoc:
|
|
68
68
|
attr_reader :recipients
|
69
69
|
|
70
70
|
# @param recipients [Array<#call, Array(Object, Symbol)>]
|
71
|
-
|
71
|
+
# @param enable_listening [true, false]
|
72
|
+
def initialize(*recipients, enable_listening: true)
|
72
73
|
@recipients = Concurrent::Array.new(recipients)
|
74
|
+
@connected = Concurrent::AtomicBoolean.new(false)
|
73
75
|
@listening = Concurrent::AtomicBoolean.new(false)
|
74
76
|
@connection_errors_count = Concurrent::AtomicFixnum.new(0)
|
75
77
|
@connection_errors_reported = Concurrent::AtomicBoolean.new(false)
|
78
|
+
@enable_listening = enable_listening
|
76
79
|
|
77
80
|
self.class.instances << self
|
78
81
|
|
@@ -80,7 +83,13 @@ module GoodJob # :nodoc:
|
|
80
83
|
listen
|
81
84
|
end
|
82
85
|
|
83
|
-
# Tests whether the notifier is active and
|
86
|
+
# Tests whether the notifier is active and has acquired a dedicated database connection.
|
87
|
+
# @return [true, false, nil]
|
88
|
+
def connected?
|
89
|
+
@connected.true?
|
90
|
+
end
|
91
|
+
|
92
|
+
# Tests whether the notifier is listening for new messages.
|
84
93
|
# @return [true, false, nil]
|
85
94
|
def listening?
|
86
95
|
@listening.true?
|
@@ -165,15 +174,17 @@ module GoodJob # :nodoc:
|
|
165
174
|
end
|
166
175
|
|
167
176
|
def listen(delay: 0)
|
168
|
-
future = Concurrent::ScheduledTask.new(delay, args: [@recipients, executor, @listening], executor: @executor) do |thr_recipients, thr_executor, thr_listening|
|
177
|
+
future = Concurrent::ScheduledTask.new(delay, args: [@recipients, executor, @enable_listening, @listening], executor: @executor) do |thr_recipients, thr_executor, thr_enable_listening, thr_listening|
|
169
178
|
with_connection do
|
170
179
|
begin
|
171
180
|
Rails.application.executor.wrap do
|
172
181
|
run_callbacks :listen do
|
173
|
-
|
174
|
-
|
182
|
+
if thr_enable_listening
|
183
|
+
ActiveSupport::Notifications.instrument("notifier_listen.good_job") do
|
184
|
+
connection.execute("LISTEN #{CHANNEL}")
|
185
|
+
thr_listening.make_true
|
186
|
+
end
|
175
187
|
end
|
176
|
-
thr_listening.make_true
|
177
188
|
end
|
178
189
|
end
|
179
190
|
|
@@ -195,9 +206,11 @@ module GoodJob # :nodoc:
|
|
195
206
|
ensure
|
196
207
|
Rails.application.executor.wrap do
|
197
208
|
run_callbacks :unlisten do
|
198
|
-
|
199
|
-
|
200
|
-
|
209
|
+
if thr_enable_listening
|
210
|
+
ActiveSupport::Notifications.instrument("notifier_unlisten.good_job") do
|
211
|
+
thr_listening.make_false
|
212
|
+
connection.execute("UNLISTEN *")
|
213
|
+
end
|
201
214
|
end
|
202
215
|
end
|
203
216
|
end
|
@@ -215,20 +228,22 @@ module GoodJob # :nodoc:
|
|
215
228
|
end
|
216
229
|
end
|
217
230
|
connection.execute("SET application_name = #{connection.quote(self.class.name)}")
|
231
|
+
@connected.make_true
|
218
232
|
|
219
233
|
yield
|
220
234
|
ensure
|
235
|
+
@connected.make_false
|
221
236
|
connection&.disconnect!
|
222
237
|
self.connection = nil
|
223
238
|
end
|
224
239
|
|
225
240
|
def wait_for_notify
|
226
241
|
raw_connection = connection.raw_connection
|
227
|
-
if raw_connection.respond_to?(:wait_for_notify)
|
242
|
+
if @enable_listening && raw_connection.respond_to?(:wait_for_notify)
|
228
243
|
raw_connection.wait_for_notify(WAIT_INTERVAL) do |channel, _pid, payload|
|
229
244
|
yield(channel, payload)
|
230
245
|
end
|
231
|
-
elsif raw_connection.respond_to?(:jdbc_connection)
|
246
|
+
elsif @enable_listening && raw_connection.respond_to?(:jdbc_connection)
|
232
247
|
raw_connection.execute_query("SELECT 1")
|
233
248
|
notifications = raw_connection.jdbc_connection.getNotifications
|
234
249
|
Array(notifications).each do |notification|
|
data/lib/good_job/version.rb
CHANGED
data/lib/good_job.rb
CHANGED
@@ -7,8 +7,9 @@ require "good_job/engine"
|
|
7
7
|
|
8
8
|
require "good_job/adapter"
|
9
9
|
require "active_job/queue_adapters/good_job_adapter"
|
10
|
-
require "good_job/active_job_extensions/concurrency"
|
11
10
|
require "good_job/active_job_extensions/batches"
|
11
|
+
require "good_job/active_job_extensions/concurrency"
|
12
|
+
require "good_job/active_job_extensions/notify_options"
|
12
13
|
|
13
14
|
require "good_job/assignable_connection"
|
14
15
|
require "good_job/bulk"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: good_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Sheldon
|
@@ -403,6 +403,7 @@ files:
|
|
403
403
|
- lib/good_job.rb
|
404
404
|
- lib/good_job/active_job_extensions/batches.rb
|
405
405
|
- lib/good_job/active_job_extensions/concurrency.rb
|
406
|
+
- lib/good_job/active_job_extensions/notify_options.rb
|
406
407
|
- lib/good_job/adapter.rb
|
407
408
|
- lib/good_job/assignable_connection.rb
|
408
409
|
- lib/good_job/bulk.rb
|