good_job 2.7.1 → 2.7.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad8ca019c816dee452b246cdaa6d806844bcbd6aca63da6e5d52c87828c9c8e4
4
- data.tar.gz: 343c128a0d8477688e8f38d214add4c341dbfce694c19c6c2acf9cf7bb5c5cef
3
+ metadata.gz: e4843101f6fce50527e8d0157ff7ced91fabb0ae41c3c92006abd49e9005ea78
4
+ data.tar.gz: f8f243fb7f9e3ea2ec17fc795ad5bbd79b017f68665d98b911dfe27177e13389
5
5
  SHA512:
6
- metadata.gz: d5153a01609aefefd4ac23319ba76755ba13ea64c749b9b582fda5a17a497c0d640e20e6db91aa9eb9b18042b62283acc8dd25c67f1389290291ae55c2187f33
7
- data.tar.gz: e145b2a1d26eaf4b07e60ca8826e39216de230569f197f0eafae481b7716754aeb95a4bce54330e15389612f49361f320c8934965beca9c4c60d97f1a84f3b05
6
+ metadata.gz: f2dbdd1c1811bfc09153a8eed32b95f337919d22434b004622a0ab78eb6803e68f5a66761e828bf2054421d5d5f9b798be54e7f60cc486602a997c804ab9fb2d
7
+ data.tar.gz: 953672a066c27a231fb76c3c9839b4cb9685deb0ed4f25a3e153f4ede0833afa0a263f4d37532715a62ed7a96825384b8f7d6fef7352416d0c64068864798fee
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [v2.7.2](https://github.com/bensheldon/good_job/tree/v2.7.2) (2021-11-29)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v2.7.1...v2.7.2)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - Allow GoodJob global configuration accessors to also be set via Rails config hash [\#460](https://github.com/bensheldon/good_job/pull/460) ([bensheldon](https://github.com/bensheldon))
10
+
11
+ **Merged pull requests:**
12
+
13
+ - Use `ActiveRecord::Relation::QueryAttribute` when setting up bindings for `exec_query` [\#461](https://github.com/bensheldon/good_job/pull/461) ([bensheldon](https://github.com/bensheldon))
14
+ - Configure RSpec `config.example_status_persistence_file_path` [\#459](https://github.com/bensheldon/good_job/pull/459) ([bensheldon](https://github.com/bensheldon))
15
+ - Defer async initialization until Rails fully initialized [\#454](https://github.com/bensheldon/good_job/pull/454) ([bensheldon](https://github.com/bensheldon))
16
+
3
17
  ## [v2.7.1](https://github.com/bensheldon/good_job/tree/v2.7.1) (2021-11-26)
4
18
 
5
19
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v2.7.0...v2.7.1)
data/README.md CHANGED
@@ -97,7 +97,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
97
97
  1. Configure the ActiveJob adapter:
98
98
 
99
99
  ```ruby
100
- # config/application.rb
100
+ # config/application.rb or config/environments/{RAILS_ENV}.rb
101
101
  config.active_job.queue_adapter = :good_job
102
102
  ```
103
103
 
@@ -212,43 +212,48 @@ to delete old records and preserve space in your database.
212
212
 
213
213
  ### Configuration options
214
214
 
215
- To use GoodJob, you can set `config.active_job.queue_adapter` to a `:good_job`.
215
+ ActiveJob configuration depends on where the code is placed:
216
216
 
217
- Additional configuration can be provided via `config.good_job.OPTION = ...`.
217
+ - `config.active_job.queue_adapter = :good_job` within `config/application.rb` or `config/environments/*.rb`.
218
+ - `ActiveJob::Base.queue_adapter = :good_job` within an initializer (e.g. `config/initializers/active_job.rb`).
218
219
 
219
- _Configuration **must** be placed into `config/application.rb` or `config/environments/{RAILS_ENV}.rb`; configuration may not work correctly if placed into `config/initializers/*.rb` because application initializers run _after_ gem initialization (see [Rails#36650](https://github.com/rails/rails/issues/36650) and [GoodJob#380](https://github.com/bensheldon/good_job/issues/380))._
220
+ GoodJob configuration can be placed within Rails `config` directory for all environments (`config/application.rb`), within a particular environment (e.g. `config/environments/development.rb`), or within an initializer (e.g. `config/initializers/good_job.rb`).
220
221
 
221
222
  Configuration examples:
222
223
 
223
224
  ```ruby
224
- # config/application.rb
225
-
226
- config.active_job.queue_adapter = :good_job
227
-
228
- # Configure options individually...
229
- config.good_job.execution_mode = :async
230
- config.good_job.max_threads = 5
231
- config.good_job.poll_interval = 30 # seconds
232
- config.good_job.shutdown_timeout = 25 # seconds
233
- config.good_job.enable_cron = true
234
- config.good_job.cron = { example: { cron: '0 * * * *', class: 'ExampleJob' } }
235
- config.good_job.queues = '*'
236
-
237
- # ...or all at once.
238
- config.good_job = {
239
- execution_mode: :async,
240
- max_threads: 5,
241
- poll_interval: 30,
242
- shutdown_timeout: 25,
243
- enable_cron: true,
244
- cron: {
245
- example: {
246
- cron: '0 * * * *',
247
- class: 'ExampleJob'
225
+ Rails.application.configure do
226
+ # Configure options individually...
227
+ config.good_job.preserve_job_records = true
228
+ config.good_job.retry_on_unhandled_error = false
229
+ config.good_job.on_thread_error = -> (exception) { Raven.capture_exception(exception) }
230
+ config.good_job.execution_mode = :async
231
+ config.good_job.max_threads = 5
232
+ config.good_job.poll_interval = 30 # seconds
233
+ config.good_job.shutdown_timeout = 25 # seconds
234
+ config.good_job.enable_cron = true
235
+ config.good_job.cron = { example: { cron: '0 * * * *', class: 'ExampleJob' } }
236
+ config.good_job.queues = '*'
237
+
238
+ # ...or all at once.
239
+ config.good_job = {
240
+ preserve_job_records: true,
241
+ retry_on_unhandled_error: false,
242
+ on_thread_error: -> (exception) { Raven.capture_exception(exception) },
243
+ execution_mode: :async,
244
+ max_threads: 5,
245
+ poll_interval: 30,
246
+ shutdown_timeout: 25,
247
+ enable_cron: true,
248
+ cron: {
249
+ example: {
250
+ cron: '0 * * * *',
251
+ class: 'ExampleJob'
252
+ },
248
253
  },
249
- },
250
- queues: '*',
251
- }
254
+ queues: '*',
255
+ }
256
+ end
252
257
  ```
253
258
 
254
259
  Available configuration options are:
@@ -265,6 +270,14 @@ Available configuration options are:
265
270
  - `shutdown_timeout` (float) 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`.
266
271
  - `enable_cron` (boolean) whether to run cron process. Defaults to `false`. You can also set this with the environment variable `GOOD_JOB_ENABLE_CRON`.
267
272
  - `cron` (hash) cron configuration. Defaults to `{}`. You can also set this as a JSON string with the environment variable `GOOD_JOB_CRON`
273
+ - `logger` ([Rails Logger](https://api.rubyonrails.org/classes/ActiveSupport/Logger.html)) lets you set a custom logger for GoodJob. It should be an instance of a Rails `Logger` (Default: `Rails.logger`).
274
+ - `preserve_job_records` (boolean) keeps job records in your database even after jobs are completed. (Default: `false`)
275
+ - `retry_on_unhandled_error` (boolean) causes jobs to be re-queued and retried if they raise an instance of `StandardError`. Instances of `Exception`, like SIGINT, will *always* be retried, regardless of this attribute’s value. (Default: `true`)
276
+ - `on_thread_error` (proc, lambda, or callable) will be called when an Exception. It can be useful for logging errors to bug tracking services, like Sentry or Airbrake. Example:
277
+
278
+ ```ruby
279
+ config.good_job.on_thread_error = -> (exception) { Raven.capture_exception(exception) }
280
+ ```
268
281
 
269
282
  By default, GoodJob configures the following execution modes per environment:
270
283
 
@@ -285,7 +298,7 @@ config.good_job.execution_mode = :external
285
298
 
286
299
  ### Global options
287
300
 
288
- Good Job’s general behavior can also be configured via several attributes directly on the `GoodJob` module:
301
+ Good Job’s general behavior can also be configured via attributes directly on the `GoodJob` module:
289
302
 
290
303
  - **`GoodJob.active_record_parent_class`** (string) The ActiveRecord parent class inherited by GoodJob's ActiveRecord model `GoodJob::Job` (defaults to `"ActiveRecord::Base"`). Configure this when using [multiple databases with ActiveRecord](https://guides.rubyonrails.org/active_record_multiple_databases.html) or when other custom configuration is necessary for the ActiveRecord model to connect to the Postgres database. _The value must be a String to avoid premature initialization of ActiveRecord._
291
304
  - **`GoodJob.logger`** ([Rails Logger](https://api.rubyonrails.org/classes/ActiveSupport/Logger.html)) lets you set a custom logger for GoodJob. It should be an instance of a Rails `Logger`.
@@ -30,7 +30,10 @@ module GoodJob
30
30
  ORDER BY timestamp ASC
31
31
  SQL
32
32
 
33
- binds = [[nil, start_time], [nil, end_time]]
33
+ binds = [
34
+ ActiveRecord::Relation::QueryAttribute.new('start_time', start_time, ActiveRecord::Type::DateTime.new),
35
+ ActiveRecord::Relation::QueryAttribute.new('end_time', end_time, ActiveRecord::Type::DateTime.new),
36
+ ]
34
37
  executions_data = GoodJob::Execution.connection.exec_query(GoodJob::Execution.pg_or_jdbc_query(count_query), "GoodJob Dashboard Chart", binds)
35
38
 
36
39
  queue_names = executions_data.reject { |d| d['count'].nil? }.map { |d| d['queue_name'] || BaseFilter::EMPTY }.uniq
@@ -4,6 +4,12 @@ module GoodJob
4
4
  # ActiveJob Adapter.
5
5
  #
6
6
  class Adapter
7
+ # @!attribute [r] instances
8
+ # @!scope class
9
+ # List of all instantiated Adapters in the current process.
10
+ # @return [Array<GoodJob::Adapter>, nil]
11
+ cattr_reader :instances, default: [], instance_reader: false
12
+
7
13
  # @param execution_mode [Symbol, nil] specifies how and where jobs should be executed. You can also set this with the environment variable +GOOD_JOB_EXECUTION_MODE+.
8
14
  #
9
15
  # - +:inline+ executes jobs immediately in whatever process queued them (usually the web server process). This should only be used in test and development environments.
@@ -20,7 +26,8 @@ module GoodJob
20
26
  # @param max_threads [Integer, nil] sets the number of threads per scheduler to use when +execution_mode+ is set to +:async+. The +queues+ parameter can specify a number of threads for each group of queues which will override this value. You can also set this with the environment variable +GOOD_JOB_MAX_THREADS+. Defaults to +5+.
21
27
  # @param queues [String, nil] determines which queues to execute jobs from when +execution_mode+ is set to +:async+. See {file:README.md#optimize-queues-threads-and-processes} for more details on the format of this string. You can also set this with the environment variable +GOOD_JOB_QUEUES+. Defaults to +"*"+.
22
28
  # @param poll_interval [Integer, nil] sets the number of seconds between polls for jobs when +execution_mode+ is set to +:async+. You can also set this with the environment variable +GOOD_JOB_POLL_INTERVAL+. Defaults to +1+.
23
- def initialize(execution_mode: nil, queues: nil, max_threads: nil, poll_interval: nil)
29
+ # @param start_async_on_initialize [Boolean] whether to start the async scheduler when the adapter is initialized.
30
+ def initialize(execution_mode: nil, queues: nil, max_threads: nil, poll_interval: nil, start_async_on_initialize: Rails.application.initialized?)
24
31
  @configuration = GoodJob::Configuration.new(
25
32
  {
26
33
  execution_mode: execution_mode,
@@ -30,16 +37,9 @@ module GoodJob
30
37
  }
31
38
  )
32
39
  @configuration.validate!
40
+ self.class.instances << self
33
41
 
34
- if execute_async? # rubocop:disable Style/GuardClause
35
- @notifier = GoodJob::Notifier.new
36
- @poller = GoodJob::Poller.new(poll_interval: @configuration.poll_interval)
37
- @scheduler = GoodJob::Scheduler.from_configuration(@configuration, warm_cache_on_initialize: Rails.application.initialized?)
38
- @notifier.recipients << [@scheduler, :create_thread]
39
- @poller.recipients << [@scheduler, :create_thread]
40
-
41
- @cron_manager = GoodJob::CronManager.new(@configuration.cron_entries, start_on_initialize: Rails.application.initialized?) if @configuration.enable_cron?
42
- end
42
+ start_async if start_async_on_initialize
43
43
  end
44
44
 
45
45
  # Enqueues the ActiveJob job to be performed.
@@ -74,7 +74,7 @@ module GoodJob
74
74
  job_state = { queue_name: execution.queue_name }
75
75
  job_state[:scheduled_at] = execution.scheduled_at if execution.scheduled_at
76
76
 
77
- executed_locally = execute_async? && @scheduler.create_thread(job_state)
77
+ executed_locally = execute_async? && @scheduler&.create_thread(job_state)
78
78
  Notifier.notify(job_state) unless executed_locally
79
79
  end
80
80
 
@@ -97,6 +97,7 @@ module GoodJob
97
97
 
98
98
  executables = [@notifier, @poller, @scheduler].compact
99
99
  GoodJob._shutdown_all(executables, timeout: timeout)
100
+ @_async_started = false
100
101
  end
101
102
 
102
103
  # Whether in +:async+ execution mode.
@@ -119,6 +120,28 @@ module GoodJob
119
120
  @configuration.execution_mode == :inline
120
121
  end
121
122
 
123
+ # Start async executors
124
+ # @return void
125
+ def start_async
126
+ return unless execute_async?
127
+
128
+ @notifier = GoodJob::Notifier.new
129
+ @poller = GoodJob::Poller.new(poll_interval: @configuration.poll_interval)
130
+ @scheduler = GoodJob::Scheduler.from_configuration(@configuration, warm_cache_on_initialize: true)
131
+ @notifier.recipients << [@scheduler, :create_thread]
132
+ @poller.recipients << [@scheduler, :create_thread]
133
+
134
+ @cron_manager = GoodJob::CronManager.new(@configuration.cron_entries, start_on_initialize: true) if @configuration.enable_cron?
135
+
136
+ @_async_started = true
137
+ end
138
+
139
+ # Whether the async executors are running
140
+ # @return [Boolean]
141
+ def async_started?
142
+ @_async_started
143
+ end
144
+
122
145
  private
123
146
 
124
147
  # Whether running in a web server process.
data/lib/good_job/cli.rb CHANGED
@@ -96,6 +96,7 @@ module GoodJob
96
96
  poller.recipients << [scheduler, :create_thread]
97
97
 
98
98
  cron_manager = GoodJob::CronManager.new(configuration.cron_entries, start_on_initialize: true) if configuration.enable_cron?
99
+
99
100
  if configuration.probe_port
100
101
  probe_server = GoodJob::ProbeServer.new(port: configuration.probe_port)
101
102
  probe_server.start
@@ -50,24 +50,22 @@ module GoodJob
50
50
  # for more details on possible values.
51
51
  # @return [Symbol]
52
52
  def execution_mode
53
- @_execution_mode ||= begin
54
- mode = if GoodJob::CLI.within_exe?
55
- :external
56
- else
57
- options[:execution_mode] ||
58
- rails_config[:execution_mode] ||
59
- env['GOOD_JOB_EXECUTION_MODE']
60
- end
61
-
62
- if mode
63
- mode.to_sym
64
- elsif Rails.env.development?
65
- :async
66
- elsif Rails.env.test?
67
- :inline
68
- else
69
- :external
70
- end
53
+ mode = if GoodJob::CLI.within_exe?
54
+ :external
55
+ else
56
+ options[:execution_mode] ||
57
+ rails_config[:execution_mode] ||
58
+ env['GOOD_JOB_EXECUTION_MODE']
59
+ end
60
+
61
+ if mode
62
+ mode.to_sym
63
+ elsif Rails.env.development?
64
+ :async
65
+ elsif Rails.env.test?
66
+ :inline
67
+ else
68
+ :external
71
69
  end
72
70
  end
73
71
 
@@ -21,7 +21,7 @@ module GoodJob # :nodoc:
21
21
  def self.task_observer(time, output, thread_error) # rubocop:disable Lint/UnusedMethodArgument
22
22
  return if thread_error.is_a? Concurrent::CancelledOperationError
23
23
 
24
- GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
24
+ GoodJob._on_thread_error(thread_error) if thread_error
25
25
  end
26
26
 
27
27
  # Execution configuration to be scheduled
@@ -215,7 +215,9 @@ module GoodJob
215
215
  SQL
216
216
  end
217
217
 
218
- binds = [[nil, key]]
218
+ binds = [
219
+ ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
220
+ ]
219
221
  self.class.connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Lock', binds).first['locked']
220
222
  end
221
223
 
@@ -229,7 +231,9 @@ module GoodJob
229
231
  query = <<~SQL.squish
230
232
  SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint) AS unlocked
231
233
  SQL
232
- binds = [[nil, key]]
234
+ binds = [
235
+ ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
236
+ ]
233
237
  self.class.connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Unlock', binds).first['unlocked']
234
238
  end
235
239
 
@@ -279,7 +283,10 @@ module GoodJob
279
283
  AND pg_locks.classid = ('x' || substr(md5($1::text), 1, 16))::bit(32)::int
280
284
  AND pg_locks.objid = (('x' || substr(md5($2::text), 1, 16))::bit(64) << 32)::bit(32)::int
281
285
  SQL
282
- binds = [[nil, key], [nil, key]]
286
+ binds = [
287
+ ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
288
+ ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
289
+ ]
283
290
  self.class.connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Locked?', binds).any?
284
291
  end
285
292
 
@@ -303,7 +310,10 @@ module GoodJob
303
310
  AND pg_locks.objid = (('x' || substr(md5($2::text), 1, 16))::bit(64) << 32)::bit(32)::int
304
311
  AND pg_locks.pid = pg_backend_pid()
305
312
  SQL
306
- binds = [[nil, key], [nil, key]]
313
+ binds = [
314
+ ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
315
+ ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
316
+ ]
307
317
  self.class.connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Owns Advisory Lock?', binds).any?
308
318
  end
309
319
 
@@ -120,7 +120,7 @@ module GoodJob # :nodoc:
120
120
  return if thread_error.is_a? AdapterCannotListenError
121
121
 
122
122
  if thread_error
123
- GoodJob.on_thread_error.call(thread_error) if GoodJob.on_thread_error.respond_to?(:call)
123
+ GoodJob._on_thread_error(thread_error)
124
124
  ActiveSupport::Notifications.instrument("notifier_notify_error.good_job", { error: thread_error })
125
125
 
126
126
  connection_error = CONNECTION_ERRORS.any? do |error_string|
@@ -91,7 +91,7 @@ module GoodJob # :nodoc:
91
91
  # @param thread_error [Exception, nil]
92
92
  # @return [void]
93
93
  def timer_observer(time, executed_task, thread_error)
94
- GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
94
+ GoodJob._on_thread_error(thread_error) if thread_error
95
95
  ActiveSupport::Notifications.instrument("finished_timer_task", { result: executed_task, error: thread_error, time: time })
96
96
  end
97
97
 
@@ -7,7 +7,7 @@ module GoodJob
7
7
  def self.task_observer(time, output, thread_error) # rubocop:disable Lint/UnusedMethodArgument
8
8
  return if thread_error.is_a? Concurrent::CancelledOperationError
9
9
 
10
- GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
10
+ GoodJob._on_thread_error(thread_error) if thread_error
11
11
  end
12
12
 
13
13
  def initialize(port:)
@@ -7,7 +7,7 @@ module GoodJob
7
7
 
8
8
  initializer "good_job.logger" do |_app|
9
9
  ActiveSupport.on_load(:good_job) do
10
- self.logger = ::Rails.logger
10
+ self.logger = ::Rails.logger if GoodJob.logger == GoodJob::DEFAULT_LOGGER
11
11
  end
12
12
  GoodJob::LogSubscriber.attach_to :good_job
13
13
  end
@@ -22,9 +22,19 @@ module GoodJob
22
22
  end
23
23
  end
24
24
 
25
- config.after_initialize do
26
- GoodJob::Scheduler.instances.each(&:warm_cache)
27
- GoodJob::CronManager.instances.each(&:start)
25
+ initializer 'good_job.rails_config' do
26
+ config.after_initialize do
27
+ GoodJob.logger = Rails.application.config.good_job.logger unless Rails.application.config.good_job.logger.nil?
28
+ GoodJob.on_thread_error = Rails.application.config.good_job.on_thread_error unless Rails.application.config.good_job.on_thread_error.nil?
29
+ GoodJob.preserve_job_records = Rails.application.config.good_job.preserve_job_records unless Rails.application.config.good_job.preserve_job_records.nil?
30
+ GoodJob.retry_on_unhandled_error = Rails.application.config.good_job.retry_on_unhandled_error unless Rails.application.config.good_job.retry_on_unhandled_error.nil?
31
+ end
32
+ end
33
+
34
+ initializer "good_job.start_async" do
35
+ config.after_initialize do
36
+ GoodJob::Adapter.instances.each(&:start_async)
37
+ end
28
38
  end
29
39
  end
30
40
  end
@@ -169,7 +169,7 @@ module GoodJob # :nodoc:
169
169
  # @return [void]
170
170
  def task_observer(time, output, thread_error)
171
171
  error = thread_error || (output.is_a?(GoodJob::ExecutionResult) ? output.unhandled_error : nil)
172
- GoodJob.on_thread_error.call(error) if error && GoodJob.on_thread_error.respond_to?(:call)
172
+ GoodJob._on_thread_error(error) if error
173
173
 
174
174
  instrument("finished_job_task", { result: output, error: thread_error, time: time })
175
175
  create_task if output
@@ -206,7 +206,7 @@ module GoodJob # :nodoc:
206
206
  end
207
207
 
208
208
  observer = lambda do |_time, _output, thread_error|
209
- GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
209
+ GoodJob._on_thread_error(thread_error) if thread_error
210
210
  create_task # If cache-warming exhausts the threads, ensure there isn't an executable task remaining
211
211
  end
212
212
  future.add_observer(observer, :call)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
3
  # GoodJob gem version.
4
- VERSION = '2.7.1'
4
+ VERSION = '2.7.2'
5
5
  end
data/lib/good_job.rb CHANGED
@@ -18,6 +18,8 @@ require "good_job/railtie"
18
18
  #
19
19
  # +GoodJob+ is the top-level namespace and exposes configuration attributes.
20
20
  module GoodJob
21
+ DEFAULT_LOGGER = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new($stdout))
22
+
21
23
  # @!attribute [rw] active_record_parent_class
22
24
  # @!scope class
23
25
  # The ActiveRecord parent class inherited by +GoodJob::Execution+ (default: +ActiveRecord::Base+).
@@ -34,7 +36,7 @@ module GoodJob
34
36
  # @return [Logger, nil]
35
37
  # @example Output GoodJob logs to a file:
36
38
  # GoodJob.logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new("log/my_logs.log"))
37
- mattr_accessor :logger, default: ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new($stdout))
39
+ mattr_accessor :logger, default: DEFAULT_LOGGER
38
40
 
39
41
  # @!attribute [rw] preserve_job_records
40
42
  # @!scope class
@@ -66,6 +68,13 @@ module GoodJob
66
68
  # @return [Proc, nil]
67
69
  mattr_accessor :on_thread_error, default: nil
68
70
 
71
+ # Called with exception when a GoodJob thread raises an exception
72
+ # @param exception [Exception] Exception that was raised
73
+ # @return [void]
74
+ def self._on_thread_error(exception)
75
+ on_thread_error.call(exception) if on_thread_error.respond_to?(:call)
76
+ end
77
+
69
78
  # Stop executing jobs.
70
79
  # GoodJob does its work in pools of background threads.
71
80
  # When forking processes you should shut down these background threads before forking, and restart them after forking.
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: 2.7.1
4
+ version: 2.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Sheldon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-11-26 00:00:00.000000000 Z
11
+ date: 2021-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob