good_job 2.7.0 → 2.7.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f44a377e368104479ccd928977d0c99c673e468fdcce61530cdfef1ee045c61d
4
- data.tar.gz: 3a5b29ba222be53ee571036ed27275517cb810a03bbd9b6fa5733b215742ef50
3
+ metadata.gz: c43ea987ac7a83911835cc6d6232839eba41f614b888dcb9752ae085cb4e30a0
4
+ data.tar.gz: eb7efe2336fa441d8c5b0e64fa43b017078c4306599bc30f829610ef24ca037a
5
5
  SHA512:
6
- metadata.gz: 833b35f3faa5660d45e14adbb15d2b6f65c7a7cbc127ae0d6d9085844e55fe33c6c1b02cbb6196f5f753bdf7ec76cff18a1976cef3ca26288960f6028c6d3fac
7
- data.tar.gz: def64d311e93139e7232fddd9aec1f0c0e0ff49696680dede67f87705d3a0dbc9338d083f1f2a22fdfff572465032c6ec75c97dee1ece7ffac20ca46c76fdc6c
6
+ metadata.gz: 3695aaebdb614804fa6df0210c411cc45120880674663551e6f53036bd237d6b64b12e2728bf810fac18c76f1372eb40de57f4b4daae24eda5448177218ffe63
7
+ data.tar.gz: d03963f9186b5c177fbd107107c4f2dcd646df4dbc96fbf9a08e91f91af01d01369a89b44ee7bbf97baa0d941c47550fa4b29e3eca326c36fdc72b21646c0796
data/CHANGELOG.md CHANGED
@@ -1,5 +1,59 @@
1
1
  # Changelog
2
2
 
3
+ ## [v2.7.4](https://github.com/bensheldon/good_job/tree/v2.7.4) (2021-12-16)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v2.7.3...v2.7.4)
6
+
7
+ **Closed issues:**
8
+
9
+ - Add nonce: true to engine views [\#467](https://github.com/bensheldon/good_job/issues/467)
10
+ - Updating good\_job breaks my Rails 7 alpha 2 local development [\#462](https://github.com/bensheldon/good_job/issues/462)
11
+
12
+ **Merged pull requests:**
13
+
14
+ - Add nonce: true to javascript\_include\_tag in dashboard [\#468](https://github.com/bensheldon/good_job/pull/468) ([bouk](https://github.com/bouk))
15
+ - Update appraisal for Rails 7.0.0.rc1 [\#466](https://github.com/bensheldon/good_job/pull/466) ([bensheldon](https://github.com/bensheldon))
16
+
17
+ ## [v2.7.3](https://github.com/bensheldon/good_job/tree/v2.7.3) (2021-11-30)
18
+
19
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v2.7.2...v2.7.3)
20
+
21
+ **Fixed bugs:**
22
+
23
+ - Logger error on 2.7.2 [\#463](https://github.com/bensheldon/good_job/issues/463)
24
+ - Fix Railtie configuration assignment when Rails configuration is a Hash, not an OrderedOptions [\#464](https://github.com/bensheldon/good_job/pull/464) ([bensheldon](https://github.com/bensheldon))
25
+
26
+ ## [v2.7.2](https://github.com/bensheldon/good_job/tree/v2.7.2) (2021-11-29)
27
+
28
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v2.7.1...v2.7.2)
29
+
30
+ **Implemented enhancements:**
31
+
32
+ - 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))
33
+
34
+ **Merged pull requests:**
35
+
36
+ - 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))
37
+ - Configure RSpec `config.example_status_persistence_file_path` [\#459](https://github.com/bensheldon/good_job/pull/459) ([bensheldon](https://github.com/bensheldon))
38
+ - Defer async initialization until Rails fully initialized [\#454](https://github.com/bensheldon/good_job/pull/454) ([bensheldon](https://github.com/bensheldon))
39
+
40
+ ## [v2.7.1](https://github.com/bensheldon/good_job/tree/v2.7.1) (2021-11-26)
41
+
42
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v2.7.0...v2.7.1)
43
+
44
+ **Fixed bugs:**
45
+
46
+ - Unclear error when database can't be reached [\#457](https://github.com/bensheldon/good_job/issues/457)
47
+ - Remove Concurrent::Delay wrapping of database-loading methods [\#458](https://github.com/bensheldon/good_job/pull/458) ([bensheldon](https://github.com/bensheldon))
48
+ - Do not delete csp policies when checking csp policies [\#456](https://github.com/bensheldon/good_job/pull/456) ([JonathanFrias](https://github.com/JonathanFrias))
49
+
50
+ **Closed issues:**
51
+
52
+ - How to suppress job scheduler logs? [\#455](https://github.com/bensheldon/good_job/issues/455)
53
+ - Configuration in environments/\*.rb overrides application.rb [\#453](https://github.com/bensheldon/good_job/issues/453)
54
+ - Testing jobs synchronously [\#435](https://github.com/bensheldon/good_job/issues/435)
55
+ - HTTP health check endpoint [\#403](https://github.com/bensheldon/good_job/issues/403)
56
+
3
57
  ## [v2.7.0](https://github.com/bensheldon/good_job/tree/v2.7.0) (2021-11-10)
4
58
 
5
59
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v2.6.2...v2.7.0)
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/good_job.svg)](https://rubygems.org/gems/good_job)
4
4
  [![Test Status](https://github.com/bensheldon/good_job/workflows/Test/badge.svg)](https://github.com/bensheldon/good_job/actions)
5
+ [![Ruby Toolbox](https://img.shields.io/badge/dynamic/json?color=blue&label=Ruby%20Toolbox&query=%24.projects%5B0%5D.score&url=https%3A%2F%2Fwww.ruby-toolbox.com%2Fapi%2Fprojects%2Fcompare%2Fgood_job&logo=data:image/svg+xml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhcyIgZGF0YS1pY29uPSJmbGFzayIgY2xhc3M9InN2Zy1pbmxpbmUtLWZhIGZhLWZsYXNrIGZhLXctMTQiIHJvbGU9ImltZyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgNDQ4IDUxMiI+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik00MzcuMiA0MDMuNUwzMjAgMjE1VjY0aDhjMTMuMyAwIDI0LTEwLjcgMjQtMjRWMjRjMC0xMy4zLTEwLjctMjQtMjQtMjRIMTIwYy0xMy4zIDAtMjQgMTAuNy0yNCAyNHYxNmMwIDEzLjMgMTAuNyAyNCAyNCAyNGg4djE1MUwxMC44IDQwMy41Qy0xOC41IDQ1MC42IDE1LjMgNTEyIDcwLjkgNTEyaDMwNi4yYzU1LjcgMCA4OS40LTYxLjUgNjAuMS0xMDguNXpNMTM3LjkgMzIwbDQ4LjItNzcuNmMzLjctNS4yIDUuOC0xMS42IDUuOC0xOC40VjY0aDY0djE2MGMwIDYuOSAyLjIgMTMuMiA1LjggMTguNGw0OC4yIDc3LjZoLTE3MnoiPjwvcGF0aD48L3N2Zz4=)](https://www.ruby-toolbox.com/projects/good_job)
5
6
 
6
7
  GoodJob is a multithreaded, Postgres-based, ActiveJob backend for Ruby on Rails.
7
8
 
@@ -55,7 +56,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
55
56
  - [Migrate to GoodJob from a different ActiveJob backend](#migrate-to-goodjob-from-a-different-activejob-backend)
56
57
  - [Monitor and preserve worked jobs](#monitor-and-preserve-worked-jobs)
57
58
  - [PgBouncer compatibility](#pgbouncer-compatibility)
58
- - [CLI HTTP health check probes](#cli-http-healthcheck-probes)
59
+ - [CLI HTTP health check probes](#cli-http-health-check-probes)
59
60
  - [Contribute](#contribute)
60
61
  - [Gem development](#gem-development)
61
62
  - [Release](#release)
@@ -97,7 +98,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
97
98
  1. Configure the ActiveJob adapter:
98
99
 
99
100
  ```ruby
100
- # config/application.rb
101
+ # config/application.rb or config/environments/{RAILS_ENV}.rb
101
102
  config.active_job.queue_adapter = :good_job
102
103
  ```
103
104
 
@@ -212,43 +213,48 @@ to delete old records and preserve space in your database.
212
213
 
213
214
  ### Configuration options
214
215
 
215
- To use GoodJob, you can set `config.active_job.queue_adapter` to a `:good_job`.
216
+ ActiveJob configuration depends on where the code is placed:
216
217
 
217
- Additional configuration can be provided via `config.good_job.OPTION = ...`.
218
+ - `config.active_job.queue_adapter = :good_job` within `config/application.rb` or `config/environments/*.rb`.
219
+ - `ActiveJob::Base.queue_adapter = :good_job` within an initializer (e.g. `config/initializers/active_job.rb`).
218
220
 
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))._
221
+ 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
222
 
221
223
  Configuration examples:
222
224
 
223
225
  ```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'
226
+ Rails.application.configure do
227
+ # Configure options individually...
228
+ config.good_job.preserve_job_records = true
229
+ config.good_job.retry_on_unhandled_error = false
230
+ config.good_job.on_thread_error = -> (exception) { Raven.capture_exception(exception) }
231
+ config.good_job.execution_mode = :async
232
+ config.good_job.max_threads = 5
233
+ config.good_job.poll_interval = 30 # seconds
234
+ config.good_job.shutdown_timeout = 25 # seconds
235
+ config.good_job.enable_cron = true
236
+ config.good_job.cron = { example: { cron: '0 * * * *', class: 'ExampleJob' } }
237
+ config.good_job.queues = '*'
238
+
239
+ # ...or all at once.
240
+ config.good_job = {
241
+ preserve_job_records: true,
242
+ retry_on_unhandled_error: false,
243
+ on_thread_error: -> (exception) { Raven.capture_exception(exception) },
244
+ execution_mode: :async,
245
+ max_threads: 5,
246
+ poll_interval: 30,
247
+ shutdown_timeout: 25,
248
+ enable_cron: true,
249
+ cron: {
250
+ example: {
251
+ cron: '0 * * * *',
252
+ class: 'ExampleJob'
253
+ },
248
254
  },
249
- },
250
- queues: '*',
251
- }
255
+ queues: '*',
256
+ }
257
+ end
252
258
  ```
253
259
 
254
260
  Available configuration options are:
@@ -265,6 +271,14 @@ Available configuration options are:
265
271
  - `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
272
  - `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
273
  - `cron` (hash) cron configuration. Defaults to `{}`. You can also set this as a JSON string with the environment variable `GOOD_JOB_CRON`
274
+ - `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`).
275
+ - `preserve_job_records` (boolean) keeps job records in your database even after jobs are completed. (Default: `false`)
276
+ - `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`)
277
+ - `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:
278
+
279
+ ```ruby
280
+ config.good_job.on_thread_error = -> (exception) { Raven.capture_exception(exception) }
281
+ ```
268
282
 
269
283
  By default, GoodJob configures the following execution modes per environment:
270
284
 
@@ -285,7 +299,7 @@ config.good_job.execution_mode = :external
285
299
 
286
300
  ### Global options
287
301
 
288
- Good Job’s general behavior can also be configured via several attributes directly on the `GoodJob` module:
302
+ Good Job’s general behavior can also be configured via attributes directly on the `GoodJob` module:
289
303
 
290
304
  - **`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
305
  - **`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
@@ -6,16 +6,16 @@ module GoodJob
6
6
  around_action :switch_locale
7
7
 
8
8
  content_security_policy do |policy|
9
- policy.default_src(:none) if policy.default_src.blank?
10
- policy.connect_src(:self) if policy.connect_src.blank?
11
- policy.base_uri(:none) if policy.base_uri.blank?
12
- policy.font_src(:self) if policy.font_src.blank?
13
- policy.img_src(:self, :data) if policy.img_src.blank?
14
- policy.object_src(:none) if policy.object_src.blank?
15
- policy.script_src(:self) if policy.script_src.blank?
16
- policy.style_src(:self) if policy.style_src.blank?
17
- policy.form_action(:self) if policy.form_action.blank?
18
- policy.frame_ancestors(:none) if policy.frame_ancestors.blank?
9
+ policy.default_src(:none) if policy.default_src(*policy.default_src).blank?
10
+ policy.connect_src(:self) if policy.connect_src(*policy.connect_src).blank?
11
+ policy.base_uri(:none) if policy.base_uri(*policy.base_uri).blank?
12
+ policy.font_src(:self) if policy.font_src(*policy.font_src).blank?
13
+ policy.img_src(:self, :data) if policy.img_src(*policy.img_src).blank?
14
+ policy.object_src(:none) if policy.object_src(*policy.object_src).blank?
15
+ policy.script_src(:self) if policy.script_src(*policy.script_src).blank?
16
+ policy.style_src(:self) if policy.style_src(*policy.style_src).blank?
17
+ policy.form_action(:self) if policy.form_action(*policy.form_action).blank?
18
+ policy.frame_ancestors(:none) if policy.frame_ancestors(*policy.frame_ancestors).blank?
19
19
  end
20
20
 
21
21
  before_action do
@@ -8,11 +8,11 @@
8
8
  <%= stylesheet_link_tag bootstrap_path(format: :css, v: GoodJob::VERSION) %>
9
9
  <%= stylesheet_link_tag style_path(format: :css, v: GoodJob::VERSION) %>
10
10
 
11
- <%= javascript_include_tag bootstrap_path(format: :js, v: GoodJob::VERSION) %>
12
- <%= javascript_include_tag chartjs_path(format: :js, v: GoodJob::VERSION) %>
13
- <%= javascript_include_tag scripts_path(format: :js, v: GoodJob::VERSION) %>
11
+ <%= javascript_include_tag bootstrap_path(format: :js, v: GoodJob::VERSION), nonce: true %>
12
+ <%= javascript_include_tag chartjs_path(format: :js, v: GoodJob::VERSION), nonce: true %>
13
+ <%= javascript_include_tag scripts_path(format: :js, v: GoodJob::VERSION), nonce: true %>
14
14
 
15
- <%= javascript_include_tag rails_ujs_path(format: :js, v: GoodJob::VERSION) %>
15
+ <%= javascript_include_tag rails_ujs_path(format: :js, v: GoodJob::VERSION), nonce: true %>
16
16
  </head>
17
17
  <body>
18
18
  <nav class="navbar navbar-expand-lg navbar-light bg-light">
@@ -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
@@ -13,9 +13,6 @@ module GoodJob
13
13
  # @param queue_string [String] Queues to execute jobs from
14
14
  def initialize(queue_string)
15
15
  @queue_string = queue_string
16
-
17
- @job_query = Concurrent::Delay.new { GoodJob::Execution.queue_string(queue_string) }
18
- @parsed_queues = Concurrent::Delay.new { GoodJob::Execution.queue_parser(queue_string) }
19
16
  end
20
17
 
21
18
  # A meaningful name to identify the performer in logs and for debugging.
@@ -65,11 +62,11 @@ module GoodJob
65
62
  attr_reader :queue_string
66
63
 
67
64
  def job_query
68
- @job_query.value
65
+ @_job_query ||= GoodJob::Execution.queue_string(queue_string)
69
66
  end
70
67
 
71
68
  def parsed_queues
72
- @parsed_queues.value
69
+ @_parsed_queues ||= GoodJob::Execution.queue_parser(queue_string)
73
70
  end
74
71
  end
75
72
  end
@@ -24,7 +24,7 @@ module GoodJob
24
24
 
25
25
  included do
26
26
  # Default column to be used when creating Advisory Locks
27
- class_attribute :advisory_lockable_column, instance_accessor: false, default: Concurrent::Delay.new { primary_key }
27
+ class_attribute :advisory_lockable_column, instance_accessor: false, default: nil
28
28
 
29
29
  # Default Postgres function to be used for Advisory Locks
30
30
  class_attribute :advisory_lockable_function, default: "pg_try_advisory_lock"
@@ -161,10 +161,8 @@ module GoodJob
161
161
  end
162
162
  end
163
163
 
164
- # Allow advisory_lockable_column to be a `Concurrent::Delay`
165
164
  def _advisory_lockable_column
166
- column = advisory_lockable_column
167
- column.respond_to?(:value) ? column.value : column
165
+ advisory_lockable_column || primary_key
168
166
  end
169
167
 
170
168
  def supports_cte_materialization_specifiers?
@@ -217,7 +215,9 @@ module GoodJob
217
215
  SQL
218
216
  end
219
217
 
220
- binds = [[nil, key]]
218
+ binds = [
219
+ ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
220
+ ]
221
221
  self.class.connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Lock', binds).first['locked']
222
222
  end
223
223
 
@@ -231,7 +231,9 @@ module GoodJob
231
231
  query = <<~SQL.squish
232
232
  SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint) AS unlocked
233
233
  SQL
234
- binds = [[nil, key]]
234
+ binds = [
235
+ ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
236
+ ]
235
237
  self.class.connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Unlock', binds).first['unlocked']
236
238
  end
237
239
 
@@ -281,7 +283,10 @@ module GoodJob
281
283
  AND pg_locks.classid = ('x' || substr(md5($1::text), 1, 16))::bit(32)::int
282
284
  AND pg_locks.objid = (('x' || substr(md5($2::text), 1, 16))::bit(64) << 32)::bit(32)::int
283
285
  SQL
284
- 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
+ ]
285
290
  self.class.connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Locked?', binds).any?
286
291
  end
287
292
 
@@ -305,7 +310,10 @@ module GoodJob
305
310
  AND pg_locks.objid = (('x' || substr(md5($2::text), 1, 16))::bit(64) << 32)::bit(32)::int
306
311
  AND pg_locks.pid = pg_backend_pid()
307
312
  SQL
308
- 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
+ ]
309
317
  self.class.connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Owns Advisory Lock?', binds).any?
310
318
  end
311
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,21 @@ 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
+ rails_config = Rails.application.config.good_job
28
+
29
+ GoodJob.logger = rails_config[:logger] if rails_config.key?(:logger)
30
+ GoodJob.on_thread_error = rails_config[:on_thread_error] if rails_config.key?(:on_thread_error)
31
+ GoodJob.preserve_job_records = rails_config[:preserve_job_records] if rails_config.key?(:preserve_job_records)
32
+ GoodJob.retry_on_unhandled_error = rails_config[:retry_on_unhandled_error] if rails_config.key?(:retry_on_unhandled_error)
33
+ end
34
+ end
35
+
36
+ initializer "good_job.start_async" do
37
+ config.after_initialize do
38
+ GoodJob::Adapter.instances.each(&:start_async)
39
+ end
28
40
  end
29
41
  end
30
42
  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.0'
4
+ VERSION = '2.7.4'
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.0
4
+ version: 2.7.4
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-10 00:00:00.000000000 Z
11
+ date: 2021-12-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob