good_job 1.99.1 → 2.0.0

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: 97662bae9e8ba65e5ed3e9ba52b8b987d77974866885a548a8a3b13878045c7a
4
- data.tar.gz: 415d36696796cfb4c0ff1737c7c75e2b66bbfceea15f3a90aba55b49ee5da715
3
+ metadata.gz: 70c12a8c0e633dfc5f0ecb073c9a9993f1d7a4ab06df8f273e034b0ddf4513c1
4
+ data.tar.gz: f8d8ecd9ba7dd4cf0ad1687d519e4dd7eb87e06cb3c6a4ab1ed05586bcfe573a
5
5
  SHA512:
6
- metadata.gz: 2d293eabb7b926386b30a1348c529de6a2252c1c734b7a92a81abad2fdd791a28abe5067850c9a4f7595182afd72d52eccc0c640d7596aaddcba467a995449ca
7
- data.tar.gz: 4b981bda934dc7e58aab80bd998c16b9c8261309cf8f5ffce09253c45821f0106ef0db0249934a3649e735715d865ff6dd62232876afb7ffca76cfb4fe473179
6
+ metadata.gz: 1ff20a3ef1bf4e585d96094884cad5d682c11111d44a2d6ac61e1bb0773060cade2f4ced22008ef9dd01cb58cc477b695c02669c20505baf7f3515f42dde391d
7
+ data.tar.gz: e367c08cbf87a8203fd1d091401f49e1684fa247f86ab70fae782bbdbc826131ea0ef5b2c20589e7edfe580a257226686424c302c61fdc5d98010152ddbd52c4
data/CHANGELOG.md CHANGED
@@ -1,32 +1,5 @@
1
1
  # Changelog
2
2
 
3
- ## [v1.99.1](https://github.com/bensheldon/good_job/tree/v1.99.1) (2021-08-27)
4
-
5
- [Full Changelog](https://github.com/bensheldon/good_job/compare/v2.0.1...v1.99.1)
6
-
7
- **Closed issues:**
8
-
9
- - Does Good job support delay method? [\#344](https://github.com/bensheldon/good_job/issues/344)
10
-
11
- **Merged pull requests:**
12
-
13
- - README style/typo fixes: "web server" and possessive "Rails'" [\#350](https://github.com/bensheldon/good_job/pull/350) ([aried3r](https://github.com/aried3r))
14
- - Add examples of setting config.good\_job.queues [\#349](https://github.com/bensheldon/good_job/pull/349) ([zachmargolis](https://github.com/zachmargolis))
15
-
16
- ## [v2.0.1](https://github.com/bensheldon/good_job/tree/v2.0.1) (2021-08-24)
17
-
18
- [Full Changelog](https://github.com/bensheldon/good_job/compare/v2.0.0...v2.0.1)
19
-
20
- **Implemented enhancements:**
21
-
22
- - Suppress backtrace of ConcurrencyExceededError [\#348](https://github.com/bensheldon/good_job/pull/348) ([reczy](https://github.com/reczy))
23
-
24
- **Closed issues:**
25
-
26
- - Is there any value in seeing a backtrace for ConcurrencyExceededError? [\#347](https://github.com/bensheldon/good_job/issues/347)
27
- - Release GoodJob 2.0 [\#307](https://github.com/bensheldon/good_job/issues/307)
28
- - Unhandled ActiveJob errors should trigger GoodJob.on\_thread\_error [\#247](https://github.com/bensheldon/good_job/issues/247)
29
-
30
3
  ## [v2.0.0](https://github.com/bensheldon/good_job/tree/v2.0.0) (2021-08-24)
31
4
 
32
5
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v1.99.0...v2.0.0)
data/README.md CHANGED
@@ -86,13 +86,6 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
86
86
  $ bin/rails db:migrate
87
87
  ```
88
88
 
89
- Optional: If using Rails' multiple databases with the `migrations_paths` configuration option, use the `--database` option:
90
-
91
- ```bash
92
- bin/rails g good_job:install --database animals
93
- bin/rails db:migrate:animals
94
- ```
95
-
96
89
  1. Configure the ActiveJob adapter:
97
90
 
98
91
  ```ruby
@@ -132,7 +125,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
132
125
  - GoodJob can also be configured to execute jobs within the web server process to save on resources. This is useful for low-workloads when economy is paramount.
133
126
 
134
127
  ```
135
- $ GOOD_JOB_EXECUTION_MODE=async_server rails server
128
+ $ GOOD_JOB_EXECUTION_MODE=async rails server
136
129
  ```
137
130
 
138
131
  Additional configuration is likely necessary, see the reference below for f configuration.
@@ -220,7 +213,7 @@ Additional configuration can be provided via `config.good_job.OPTION = ...` for
220
213
  config.active_job.queue_adapter = :good_job
221
214
 
222
215
  # Configure options individually...
223
- config.good_job.execution_mode = :async_server
216
+ config.good_job.execution_mode = :async
224
217
  config.good_job.max_threads = 5
225
218
  config.good_job.poll_interval = 30 # seconds
226
219
  config.good_job.shutdown_timeout = 25 # seconds
@@ -229,7 +222,7 @@ config.good_job.cron = { example: { cron: '0 * * * *', class: 'ExampleJob' } }
229
222
 
230
223
  # ...or all at once.
231
224
  config.good_job = {
232
- execution_mode: :async_server,
225
+ execution_mode: :async,
233
226
  max_threads: 5,
234
227
  poll_interval: 30,
235
228
  shutdown_timeout: 25,
@@ -248,11 +241,11 @@ Available configuration options are:
248
241
  - `execution_mode` (symbol) specifies how and where jobs should be executed. You can also set this with the environment variable `GOOD_JOB_EXECUTION_MODE`. It can be any one of:
249
242
  - `:inline` executes jobs immediately in whatever process queued them (usually the web server process). This should only be used in test and development environments.
250
243
  - `:external` causes the adapter to enqueue jobs, but not execute them. When using this option (the default for production environments), you’ll need to use the command-line tool to actually execute your jobs.
251
- - `:async_server` executes jobs in separate threads within the Rails webserver process (`bundle exec rails server`). It can be more economical for small workloads because you don’t need a separate machine or environment for running your jobs, but if your web server is under heavy load or your jobs require a lot of resources, you should choose `:external` instead. When not in the Rails webserver, jobs will execute in `:external` mode to ensure jobs are not executed within `rails console`, `rails db:migrate`, `rails assets:prepare`, etc.
252
- - `:async` executes jobs in separate threads in _any_ Rails process.
253
- - `max_threads` (integer) sets the maximum number of threads to use when `execution_mode` is set to `:async` or `:async_server`. You can also set this with the environment variable `GOOD_JOB_MAX_THREADS`.
254
- - `queues` (string) determines which queues to execute jobs from when `execution_mode` is set to `:async` or `:async_server`. See the description of `good_job start` for more details on the format of this string. You can also set this with the environment variable `GOOD_JOB_QUEUES`.
255
- - `poll_interval` (integer) sets the number of seconds between polls for jobs when `execution_mode` is set to `:async` or `:async_server`. You can also set this with the environment variable `GOOD_JOB_POLL_INTERVAL`.
244
+ - `:async` (or `:async_server`) executes jobs in separate threads within the Rails webserver process (`bundle exec rails server`). It can be more economical for small workloads because you don’t need a separate machine or environment for running your jobs, but if your web server is under heavy load or your jobs require a lot of resources, you should choose `:external` instead. When not in the Rails webserver, jobs will execute in `:external` mode to ensure jobs are not executed within `rails console`, `rails db:migrate`, `rails assets:prepare`, etc.
245
+ - `:async_all` executes jobs in separate threads in _any_ Rails process.
246
+ - `max_threads` (integer) sets the maximum number of threads to use when `execution_mode` is set to `:async`. You can also set this with the environment variable `GOOD_JOB_MAX_THREADS`.
247
+ - `queues` (string) determines which queues to execute jobs from when `execution_mode` is set to `:async`. See the description of `good_job start` for more details on the format of this string. You can also set this with the environment variable `GOOD_JOB_QUEUES`.
248
+ - `poll_interval` (integer) 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`.
256
249
  - `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`.
257
250
  - `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`.
258
251
  - `enable_cron` (boolean) whether to run cron process. Defaults to `false`. You can also set this with the environment variable `GOOD_JOB_ENABLE_CRON`.
@@ -264,7 +257,7 @@ By default, GoodJob configures the following execution modes per environment:
264
257
 
265
258
  # config/environments/development.rb
266
259
  config.active_job.queue_adapter = :good_job
267
- config.good_job.execution_mode = :inline
260
+ config.good_job.execution_mode = :async
268
261
 
269
262
  # config/environments/test.rb
270
263
  config.active_job.queue_adapter = :good_job
@@ -426,12 +419,6 @@ To perform upgrades to the GoodJob database tables:
426
419
  bin/rails g good_job:update
427
420
  ```
428
421
 
429
- Optional: If using Rails' multiple databases with the `migrations_paths` configuration option, use the `--database` option:
430
-
431
- ```bash
432
- $ bin/rails g good_job:update --database animals
433
- ```
434
-
435
422
  1. Run the database migration locally
436
423
 
437
424
  ```bash
@@ -443,8 +430,7 @@ To perform upgrades to the GoodJob database tables:
443
430
 
444
431
  #### Upgrading v1 to v2
445
432
 
446
- GoodJob v2 introduces a new Advisory Lock key format that is different than the v1 advisory lock key format; it's therefore necessary to perform a simple, but staged production upgrade. If you are already using `>= v1.12+` no other changes are likely44
447
- necessary.
433
+ GoodJob v2 introduces a new Advisory Lock key format that is different than the v1 advisory lock key format; it's therefore necessary to perform a simple, but staged production upgrade. If you are already using `>= v1.12+` no other changes are necessary.
448
434
 
449
435
  1. Upgrade your production environment to `v1.99.x` following the minor version upgrade process, including database migrations. `v1.99` is a transitional release that is safely compatible with both `v1.x` and `v2.0.0` because it uses both `v1`- and `v2`-formatted advisory locks.
450
436
  1. Address any deprecation warnings generated by `v1.99`.
@@ -454,6 +440,7 @@ Notable changes:
454
440
 
455
441
  - Renames `:async_server` execution mode to `:async`; renames prior `:async` execution mode to `:async_all`.
456
442
  - Sets default Development environment's execution mode to `:async` with disabled polling.
443
+ - Excludes performing jobs from `enqueue_limit`'s count in `GoodJob::ActiveJobExtensions::Concurrency`.
457
444
  - Triggers `GoodJob.on_thread_error` for unhandled ActiveJob exceptions.
458
445
  - Renames `GoodJob.reperform_jobs_on_standard_error` accessor to `GoodJob.retry_on_unhandled_error`.
459
446
  - Renames `GoodJob::Adapter.shutdown(wait:)` argument to `GoodJob::Adapter.shutdown(timeout:)`.
@@ -630,11 +617,11 @@ GoodJob can execute jobs "async" in the same process as the webserver (e.g. `bin
630
617
  config.active_job.queue_adapter = :good_job
631
618
 
632
619
  # To change the execution mode
633
- config.good_job.execution_mode = :async_server
620
+ config.good_job.execution_mode = :async
634
621
 
635
622
  # Or with more configuration
636
623
  config.good_job = {
637
- execution_mode: :async_server,
624
+ execution_mode: :async,
638
625
  max_threads: 4,
639
626
  poll_interval: 30
640
627
  }
@@ -643,7 +630,7 @@ GoodJob can execute jobs "async" in the same process as the webserver (e.g. `bin
643
630
  - Or, with environment variables:
644
631
 
645
632
  ```bash
646
- $ GOOD_JOB_EXECUTION_MODE=async_server GOOD_JOB_MAX_THREADS=4 GOOD_JOB_POLL_INTERVAL=30 bin/rails server
633
+ $ GOOD_JOB_EXECUTION_MODE=async GOOD_JOB_MAX_THREADS=4 GOOD_JOB_POLL_INTERVAL=30 bin/rails server
647
634
  ```
648
635
 
649
636
  Depending on your application configuration, you may need to take additional steps:
@@ -1,23 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
  require 'rails/generators'
3
3
  require 'rails/generators/active_record'
4
-
5
4
  module GoodJob
6
5
  #
7
6
  # Rails generator used for setting up GoodJob in a Rails application.
8
7
  # Run it with +bin/rails g good_job:install+ in your console.
9
8
  #
10
9
  class InstallGenerator < Rails::Generators::Base
11
- include ActiveRecord::Generators::Migration
10
+ include Rails::Generators::Migration
12
11
 
13
- TEMPLATES = File.join(File.dirname(__FILE__), "templates/install")
14
- source_paths << TEMPLATES
12
+ class << self
13
+ delegate :next_migration_number, to: ActiveRecord::Generators::Base
14
+ end
15
15
 
16
- class_option :database, type: :string, aliases: %i(--db), desc: "The database for your migration. By default, the current environment's primary database is used."
16
+ source_paths << File.join(File.dirname(__FILE__), "templates/install")
17
17
 
18
18
  # Generates monolithic migration file that contains all database changes.
19
19
  def create_migration_file
20
- migration_template 'migrations/create_good_jobs.rb.erb', File.join(db_migrate_path, "create_good_jobs.rb")
20
+ migration_template 'migrations/create_good_jobs.rb.erb', 'db/migrate/create_good_jobs.rb'
21
21
  end
22
22
  end
23
23
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  class CreateGoodJobs < ActiveRecord::Migration[5.2]
2
3
  def change
3
4
  enable_extension 'pgcrypto'
@@ -13,9 +13,17 @@ class CreateGoodJobs < ActiveRecord::Migration[5.2]
13
13
  t.text :error
14
14
 
15
15
  t.timestamps
16
+
17
+ t.uuid :active_job_id
18
+ t.text :concurrency_key
19
+ t.text :cron_key
20
+ t.uuid :retried_good_job_id
16
21
  end
17
22
 
18
23
  add_index :good_jobs, :scheduled_at, where: "(finished_at IS NULL)", name: "index_good_jobs_on_scheduled_at"
19
24
  add_index :good_jobs, [:queue_name, :scheduled_at], where: "(finished_at IS NULL)", name: :index_good_jobs_on_queue_name_and_scheduled_at
25
+ add_index :good_jobs, [:active_job_id, :created_at], name: :index_good_jobs_on_active_job_id_and_created_at
26
+ add_index :good_jobs, :concurrency_key, where: "(finished_at IS NULL)", name: :index_good_jobs_on_concurrency_key_when_unfinished
27
+ add_index :good_jobs, [:cron_key, :created_at], name: :index_good_jobs_on_cron_key_and_created_at
20
28
  end
21
29
  end
@@ -8,20 +8,22 @@ module GoodJob
8
8
  # Run it with +bin/rails g good_job:update+ in your console.
9
9
  #
10
10
  class UpdateGenerator < Rails::Generators::Base
11
- include ActiveRecord::Generators::Migration
11
+ include Rails::Generators::Migration
12
+
13
+ class << self
14
+ delegate :next_migration_number, to: ActiveRecord::Generators::Base
15
+ end
12
16
 
13
17
  TEMPLATES = File.join(File.dirname(__FILE__), "templates/update")
14
18
  source_paths << TEMPLATES
15
19
 
16
- class_option :database, type: :string, aliases: %i(--db), desc: "The database for your migration. By default, the current environment's primary database is used."
17
-
18
20
  # Generates incremental migration files unless they already exist.
19
21
  # All migrations should be idempotent e.g. +add_index+ is guarded with +if_index_exists?+
20
22
  def update_migration_files
21
23
  migration_templates = Dir.children(File.join(TEMPLATES, 'migrations')).sort
22
24
  migration_templates.each do |template_file|
23
25
  destination_file = template_file.match(/^\d*_(.*\.rb)/)[1] # 01_create_good_jobs.rb.erb => create_good_jobs.rb
24
- migration_template "migrations/#{template_file}", File.join(db_migrate_path, destination_file), skip: true
26
+ migration_template "migrations/#{template_file}", "db/migrate/#{destination_file}", skip: true
25
27
  end
26
28
  end
27
29
  end
@@ -24,7 +24,7 @@ module GoodJob
24
24
 
25
25
  GoodJob::Job.new.with_advisory_lock(key: key, function: "pg_advisory_lock") do
26
26
  # TODO: Why is `unscoped` necessary? Nested scope is bleeding into subsequent query?
27
- enqueue_concurrency = GoodJob::Job.unscoped.where(concurrency_key: key).unfinished.count
27
+ enqueue_concurrency = GoodJob::Job.unscoped.where(concurrency_key: key).unfinished.advisory_unlocked.count
28
28
  # The job has not yet been enqueued, so check if adding it will go over the limit
29
29
  block.call unless enqueue_concurrency + 1 > limit
30
30
  end
@@ -4,16 +4,13 @@ module GoodJob
4
4
  # ActiveJob Adapter.
5
5
  #
6
6
  class Adapter
7
- # Valid execution modes.
8
- EXECUTION_MODES = [:async, :async_server, :external, :inline].freeze
9
-
10
7
  # @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+.
11
8
  #
12
9
  # - +:inline+ executes jobs immediately in whatever process queued them (usually the web server process). This should only be used in test and development environments.
13
10
  # - +:external+ causes the adapter to enqueue jobs, but not execute them. When using this option (the default for production environments), you'll need to use the command-line tool to actually execute your jobs.
14
- # - +:async_server+ executes jobs in separate threads within the Rails webserver process (`bundle exec rails server`). It can be more economical for small workloads because you don't need a separate machine or environment for running your jobs, but if your web server is under heavy load or your jobs require a lot of resources, you should choose +:external+ instead.
11
+ # - +:async+ (or +:async_server+) executes jobs in separate threads within the Rails webserver process (`bundle exec rails server`). It can be more economical for small workloads because you don't need a separate machine or environment for running your jobs, but if your web server is under heavy load or your jobs require a lot of resources, you should choose +:external+ instead.
15
12
  # When not in the Rails webserver, jobs will execute in +:external+ mode to ensure jobs are not executed within `rails console`, `rails db:migrate`, `rails assets:prepare`, etc.
16
- # - +:async+ executes jobs in any Rails process.
13
+ # - +:async_all+ executes jobs in any Rails process.
17
14
  #
18
15
  # The default value depends on the Rails environment:
19
16
  #
@@ -24,23 +21,6 @@ module GoodJob
24
21
  # @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 +"*"+.
25
22
  # @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+.
26
23
  def initialize(execution_mode: nil, queues: nil, max_threads: nil, poll_interval: nil)
27
- if caller[0..4].find { |c| c.include?("/config/application.rb") || c.include?("/config/environments/") }
28
- ActiveSupport::Deprecation.warn(<<~DEPRECATION)
29
- GoodJob no longer recommends creating a GoodJob::Adapter instance:
30
-
31
- config.active_job.queue_adapter = GoodJob::Adapter.new...
32
-
33
- Instead, configure GoodJob through configuration:
34
-
35
- config.active_job.queue_adapter = :good_job
36
- config.good_job.execution_mode = :#{execution_mode}
37
- config.good_job.max_threads = #{max_threads}
38
- config.good_job.poll_interval = #{poll_interval}
39
- # etc...
40
-
41
- DEPRECATION
42
- end
43
-
44
24
  @configuration = GoodJob::Configuration.new(
45
25
  {
46
26
  execution_mode: execution_mode,
@@ -105,18 +85,8 @@ module GoodJob
105
85
  # * +-1+, the scheduler will wait until the shutdown is complete.
106
86
  # * +0+, the scheduler will immediately shutdown and stop any threads.
107
87
  # * A positive number will wait that many seconds before stopping any remaining active threads.
108
- # @param wait [Boolean, nil] Deprecated. Use +timeout:+ instead.
109
88
  # @return [void]
110
- def shutdown(timeout: :default, wait: nil)
111
- timeout = if wait.nil?
112
- timeout
113
- else
114
- ActiveSupport::Deprecation.warn(
115
- "Using `GoodJob::Adapter.shutdown` with `wait:` kwarg is deprecated; use `timeout:` kwarg instead e.g. GoodJob::Adapter.shutdown(timeout: #{wait ? '-1' : 'nil'})"
116
- )
117
- wait ? -1 : nil
118
- end
119
-
89
+ def shutdown(timeout: :default)
120
90
  timeout = if timeout == :default
121
91
  @configuration.shutdown_timeout
122
92
  else
@@ -130,15 +100,15 @@ module GoodJob
130
100
  # Whether in +:async+ execution mode.
131
101
  # @return [Boolean]
132
102
  def execute_async?
133
- @configuration.execution_mode.in?([:async, :async_all]) ||
134
- @configuration.execution_mode == :async_server && in_server_process?
103
+ @configuration.execution_mode == :async_all ||
104
+ @configuration.execution_mode.in?([:async, :async_server]) && in_server_process?
135
105
  end
136
106
 
137
107
  # Whether in +:external+ execution mode.
138
108
  # @return [Boolean]
139
109
  def execute_externally?
140
110
  @configuration.execution_mode == :external ||
141
- @configuration.execution_mode == :async_server && !in_server_process?
111
+ @configuration.execution_mode.in?([:async, :async_server]) && !in_server_process?
142
112
  end
143
113
 
144
114
  # Whether in +:inline+ execution mode.
@@ -12,6 +12,8 @@ module GoodJob
12
12
  DEFAULT_MAX_THREADS = 5
13
13
  # Default number of seconds between polls for jobs
14
14
  DEFAULT_POLL_INTERVAL = 10
15
+ # Default poll interval for async in development environment
16
+ DEFAULT_DEVELOPMENT_ASYNC_POLL_INTERVAL = -1
15
17
  # Default number of threads to use per {Scheduler}
16
18
  DEFAULT_MAX_CACHE = 10000
17
19
  # Default number of seconds to preserve jobs for {CLI#cleanup_preserved_jobs}
@@ -58,19 +60,10 @@ module GoodJob
58
60
  end
59
61
 
60
62
  if mode
61
- mode_sym = mode.to_sym
62
- if mode_sym == :async
63
- ActiveSupport::Deprecation.warn <<~DEPRECATION
64
- The next major version of GoodJob will redefine the meaning of 'async'
65
- execution mode to be equivalent to 'async_server' and only execute
66
- within the webserver process.
67
-
68
- To continue using the v1.0 semantics of 'async', use `async_all` instead.
69
-
70
- DEPRECATION
71
- end
72
- mode_sym
73
- elsif Rails.env.development? || Rails.env.test?
63
+ mode.to_sym
64
+ elsif Rails.env.development?
65
+ :async
66
+ elsif Rails.env.test?
74
67
  :inline
75
68
  else
76
69
  :external
@@ -109,12 +102,19 @@ module GoodJob
109
102
  # poll (using this interval) for new queued jobs to execute.
110
103
  # @return [Integer]
111
104
  def poll_interval
112
- (
105
+ interval = (
113
106
  options[:poll_interval] ||
114
107
  rails_config[:poll_interval] ||
115
- env['GOOD_JOB_POLL_INTERVAL'] ||
116
- DEFAULT_POLL_INTERVAL
117
- ).to_i
108
+ env['GOOD_JOB_POLL_INTERVAL']
109
+ )
110
+
111
+ if interval
112
+ interval.to_i
113
+ elsif Rails.env.development? && execution_mode.in?([:async, :async_all, :async_server])
114
+ DEFAULT_DEVELOPMENT_ASYNC_POLL_INTERVAL
115
+ else
116
+ DEFAULT_POLL_INTERVAL
117
+ end
118
118
  end
119
119
 
120
120
  # The maximum number of future-scheduled jobs to store in memory.
@@ -147,12 +147,13 @@ module GoodJob
147
147
  def enable_cron
148
148
  value = ActiveModel::Type::Boolean.new.cast(
149
149
  options[:enable_cron] ||
150
- rails_config[:enable_cron] ||
151
- env['GOOD_JOB_ENABLE_CRON'] ||
152
- false
150
+ rails_config[:enable_cron] ||
151
+ env['GOOD_JOB_ENABLE_CRON'] ||
152
+ false
153
153
  )
154
154
  value && cron.size.positive?
155
155
  end
156
+
156
157
  alias enable_cron? enable_cron
157
158
 
158
159
  def cron
data/lib/good_job/job.rb CHANGED
@@ -16,7 +16,7 @@ module GoodJob
16
16
  DEFAULT_PRIORITY = 0
17
17
 
18
18
  self.table_name = 'good_jobs'
19
- self.advisory_lockable_column = 'id'
19
+ self.advisory_lockable_column = 'active_job_id'
20
20
 
21
21
  attr_readonly :serialized_params
22
22
 
@@ -52,20 +52,6 @@ module GoodJob
52
52
  end
53
53
  end
54
54
 
55
- def self._migration_pending_warning
56
- ActiveSupport::Deprecation.warn(<<~DEPRECATION)
57
- GoodJob has pending database migrations. To create the migration files, run:
58
-
59
- rails generate good_job:update
60
-
61
- To apply the migration files, run:
62
-
63
- rails db:migrate
64
-
65
- DEPRECATION
66
- nil
67
- end
68
-
69
55
  # Get Jobs with given class name
70
56
  # @!method with_job_class
71
57
  # @!scope class
@@ -78,14 +64,7 @@ module GoodJob
78
64
  # @!method unfinished
79
65
  # @!scope class
80
66
  # @return [ActiveRecord::Relation]
81
- scope :unfinished, (lambda do
82
- if column_names.include?('finished_at')
83
- where(finished_at: nil)
84
- else
85
- ActiveSupport::Deprecation.warn('GoodJob expects a good_jobs.finished_at column to exist. Please see the GoodJob README.md for migration instructions.')
86
- nil
87
- end
88
- end)
67
+ scope :unfinished, -> { where(finished_at: nil) }
89
68
 
90
69
  # Get Jobs that are not scheduled for a later time than now (i.e. jobs that
91
70
  # are not scheduled or scheduled for earlier than the current time).
@@ -231,6 +210,7 @@ module GoodJob
231
210
  def self.enqueue(active_job, scheduled_at: nil, create_with_advisory_lock: false)
232
211
  ActiveSupport::Notifications.instrument("enqueue_job.good_job", { active_job: active_job, scheduled_at: scheduled_at, create_with_advisory_lock: create_with_advisory_lock }) do |instrument_payload|
233
212
  good_job_args = {
213
+ active_job_id: active_job.job_id,
234
214
  queue_name: active_job.queue_name.presence || DEFAULT_QUEUE_NAME,
235
215
  priority: active_job.priority || DEFAULT_PRIORITY,
236
216
  serialized_params: active_job.serialize,
@@ -238,26 +218,12 @@ module GoodJob
238
218
  create_with_advisory_lock: create_with_advisory_lock,
239
219
  }
240
220
 
241
- if column_names.include?('active_job_id')
242
- good_job_args[:active_job_id] = active_job.job_id
243
- else
244
- _migration_pending_warning
245
- end
246
-
247
- if column_names.include?('concurrency_key')
248
- good_job_args[:concurrency_key] = active_job.good_job_concurrency_key if active_job.respond_to?(:good_job_concurrency_key)
249
- else
250
- _migration_pending_warning
251
- end
221
+ good_job_args[:concurrency_key] = active_job.good_job_concurrency_key if active_job.respond_to?(:good_job_concurrency_key)
252
222
 
253
- if column_names.include?('cron_key')
254
- if CurrentExecution.cron_key
255
- good_job_args[:cron_key] = CurrentExecution.cron_key
256
- elsif CurrentExecution.active_job_id == active_job.job_id
257
- good_job_args[:cron_key] = CurrentExecution.good_job.cron_key
258
- end
259
- else
260
- _migration_pending_warning
223
+ if CurrentExecution.cron_key
224
+ good_job_args[:cron_key] = CurrentExecution.cron_key
225
+ elsif CurrentExecution.active_job_id == active_job.job_id
226
+ good_job_args[:cron_key] = CurrentExecution.good_job.cron_key
261
227
  end
262
228
 
263
229
  good_job = GoodJob::Job.new(**good_job_args)
@@ -267,11 +233,7 @@ module GoodJob
267
233
  good_job.save!
268
234
  active_job.provider_job_id = good_job.id
269
235
 
270
- if column_names.include?('retried_good_job_id')
271
- CurrentExecution.good_job.retried_good_job_id = good_job.id if CurrentExecution.good_job && CurrentExecution.good_job.active_job_id == active_job.job_id
272
- else
273
- _migration_pending_warning
274
- end
236
+ CurrentExecution.good_job.retried_good_job_id = good_job.id if CurrentExecution.good_job && CurrentExecution.good_job.active_job_id == active_job.job_id
275
237
 
276
238
  good_job
277
239
  end
@@ -311,24 +273,6 @@ module GoodJob
311
273
  self.class.unscoped.unfinished.owns_advisory_locked.exists?(id: id)
312
274
  end
313
275
 
314
- def active_job_id
315
- if self.class.column_names.include?('active_job_id')
316
- super
317
- else
318
- self.class._migration_pending_warning
319
- serialized_params['job_id']
320
- end
321
- end
322
-
323
- def cron_key
324
- if self.class.column_names.include?('cron_key')
325
- super
326
- else
327
- self.class._migration_pending_warning
328
- nil
329
- end
330
- end
331
-
332
276
  private
333
277
 
334
278
  # @return [ExecutionResult]
@@ -51,7 +51,7 @@ module GoodJob
51
51
  composed_cte = Arel::Nodes::As.new(cte_table, Arel::Nodes::SqlLiteral.new([cte_type, "(", cte_query.to_sql, ")"].join(' ')))
52
52
  query = cte_table.project(cte_table[:id])
53
53
  .with(composed_cte)
54
- .where(Arel.sql(sanitize_sql_for_conditions(["#{function}(('x' || substr(md5(:table_name || #{connection.quote_table_name(cte_table.name)}.#{connection.quote_column_name(column)}::text), 1, 16))::bit(64)::bigint)", { table_name: table_name }])))
54
+ .where(Arel.sql(sanitize_sql_for_conditions(["#{function}(('x' || substr(md5(:table_name || '-' || #{connection.quote_table_name(cte_table.name)}.#{connection.quote_column_name(column)}::text), 1, 16))::bit(64)::bigint)", { table_name: table_name }])))
55
55
 
56
56
  limit = original_query.arel.ast.limit
57
57
  query.limit = limit.value if limit.present?
@@ -75,8 +75,8 @@ module GoodJob
75
75
  join_sql = <<~SQL.squish
76
76
  LEFT JOIN pg_locks ON pg_locks.locktype = 'advisory'
77
77
  AND pg_locks.objsubid = 1
78
- AND pg_locks.classid = ('x' || substr(md5(:table_name || #{quoted_table_name}.#{connection.quote_column_name(column)}::text), 1, 16))::bit(32)::int
79
- AND pg_locks.objid = (('x' || substr(md5(:table_name || #{quoted_table_name}.#{connection.quote_column_name(column)}::text), 1, 16))::bit(64) << 32)::bit(32)::int
78
+ AND pg_locks.classid = ('x' || substr(md5(:table_name || '-' || #{quoted_table_name}.#{connection.quote_column_name(column)}::text), 1, 16))::bit(32)::int
79
+ AND pg_locks.objid = (('x' || substr(md5(:table_name || '-' || #{quoted_table_name}.#{connection.quote_column_name(column)}::text), 1, 16))::bit(64) << 32)::bit(32)::int
80
80
  SQL
81
81
 
82
82
  joins(sanitize_sql_for_conditions([join_sql, { table_name: table_name }]))
@@ -315,7 +315,7 @@ module GoodJob
315
315
  # Default Advisory Lock key
316
316
  # @return [String]
317
317
  def lockable_key
318
- [self.class.table_name, self[self.class._advisory_lockable_column]].join
318
+ "#{self.class.table_name}-#{self[self.class._advisory_lockable_column]}"
319
319
  end
320
320
 
321
321
  delegate :pg_or_jdbc_query, to: :class
@@ -168,7 +168,9 @@ module GoodJob # :nodoc:
168
168
  # @!visibility private
169
169
  # @return [void]
170
170
  def task_observer(time, output, thread_error)
171
- GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
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)
173
+
172
174
  instrument("finished_job_task", { result: output, error: thread_error, time: time })
173
175
  create_task if output
174
176
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
3
  # GoodJob gem version.
4
- VERSION = '1.99.1'
4
+ VERSION = '2.0.0'
5
5
  end
data/lib/good_job.rb CHANGED
@@ -55,25 +55,6 @@ module GoodJob
55
55
  # @return [Boolean, nil]
56
56
  mattr_accessor :retry_on_unhandled_error, default: true
57
57
 
58
- # @deprecated Use {GoodJob#retry_on_unhandled_error} instead.
59
- # @return [Boolean, nil]
60
- def self.reperform_jobs_on_standard_error
61
- ActiveSupport::Deprecation.warn(
62
- "Calling 'GoodJob.reperform_jobs_on_standard_error' is deprecated. Please use 'retry_on_unhandled_error'"
63
- )
64
- retry_on_unhandled_error
65
- end
66
-
67
- # @deprecated Use {GoodJob#retry_on_unhandled_error=} instead.
68
- # @param value [Boolean]
69
- # @return [Boolean]
70
- def self.reperform_jobs_on_standard_error=(value)
71
- ActiveSupport::Deprecation.warn(
72
- "Setting 'GoodJob.reperform_jobs_on_standard_error=' is deprecated. Please use 'retry_on_unhandled_error='"
73
- )
74
- self.retry_on_unhandled_error = value
75
- end
76
-
77
58
  # @!attribute [rw] on_thread_error
78
59
  # @!scope class
79
60
  # This callable will be called when an exception reaches GoodJob (default: +nil+).
@@ -96,16 +77,7 @@ module GoodJob
96
77
  # * +1..+, the scheduler will wait that many seconds before stopping any remaining active tasks.
97
78
  # @param wait [Boolean] whether to wait for shutdown
98
79
  # @return [void]
99
- def self.shutdown(timeout: -1, wait: nil)
100
- timeout = if wait.nil?
101
- timeout
102
- else
103
- ActiveSupport::Deprecation.warn(
104
- "Using `GoodJob.shutdown` with `wait:` kwarg is deprecated; use `timeout:` kwarg instead e.g. GoodJob.shutdown(timeout: #{wait ? '-1' : 'nil'})"
105
- )
106
- wait ? -1 : nil
107
- end
108
-
80
+ def self.shutdown(timeout: -1)
109
81
  _shutdown_all(_executables, timeout: timeout)
110
82
  end
111
83
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: good_job
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.99.1
4
+ version: 2.0.0
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-08-27 00:00:00.000000000 Z
11
+ date: 2021-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -372,9 +372,6 @@ files:
372
372
  - lib/generators/good_job/install_generator.rb
373
373
  - lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb
374
374
  - lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb
375
- - lib/generators/good_job/templates/update/migrations/02_add_active_job_id_concurrency_key_cron_key_to_good_jobs.rb
376
- - lib/generators/good_job/templates/update/migrations/03_add_active_job_id_index_and_concurrency_key_index_to_good_jobs.rb
377
- - lib/generators/good_job/templates/update/migrations/04_add_retried_good_job_id_to_good_jobs.rb
378
375
  - lib/generators/good_job/update_generator.rb
379
376
  - lib/good_job.rb
380
377
  - lib/good_job/active_job_extensions.rb
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
- class AddActiveJobIdConcurrencyKeyCronKeyToGoodJobs < ActiveRecord::Migration[5.2]
3
- def change
4
- reversible do |dir|
5
- dir.up do
6
- # Ensure this incremental update migration is idempotent
7
- # with monolithic install migration.
8
- return if connection.column_exists?(:good_jobs, :active_job_id)
9
- end
10
- end
11
-
12
- add_column :good_jobs, :active_job_id, :uuid
13
- add_column :good_jobs, :concurrency_key, :text
14
- add_column :good_jobs, :cron_key, :text
15
- end
16
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
- class AddActiveJobIdIndexAndConcurrencyKeyIndexToGoodJobs < ActiveRecord::Migration[5.2]
3
- disable_ddl_transaction!
4
-
5
- UPDATE_BATCH_SIZE = 1_000
6
-
7
- def change
8
- reversible do |dir|
9
- dir.up do
10
- # Ensure this incremental update migration is idempotent
11
- # with monolithic install migration.
12
- return if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_active_job_id_and_created_at)
13
- end
14
- end
15
-
16
- add_index :good_jobs, [:active_job_id, :created_at], algorithm: :concurrently, name: :index_good_jobs_on_active_job_id_and_created_at
17
- add_index :good_jobs, :concurrency_key, where: "(finished_at IS NULL)", algorithm: :concurrently, name: :index_good_jobs_on_concurrency_key_when_unfinished
18
- add_index :good_jobs, [:cron_key, :created_at], algorithm: :concurrently, name: :index_good_jobs_on_cron_key_and_created_at
19
-
20
- return unless defined? GoodJob::Job
21
-
22
- reversible do |dir|
23
- dir.up do
24
- # Ensure that all `good_jobs` records have an active_job_id value
25
- start_time = Time.current
26
- loop do
27
- break if GoodJob::Job.where(active_job_id: nil, finished_at: nil).where("created_at < ?", start_time).limit(UPDATE_BATCH_SIZE).update_all("active_job_id = (serialized_params->>'job_id')::uuid").zero?
28
- end
29
- end
30
- end
31
- end
32
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
- class AddRetriedGoodJobIdToGoodJobs < ActiveRecord::Migration[5.2]
3
- def change
4
- reversible do |dir|
5
- dir.up do
6
- # Ensure this incremental update migration is idempotent
7
- # with monolithic install migration.
8
- return if connection.column_exists?(:good_jobs, :retried_good_job_id)
9
- end
10
- end
11
-
12
- add_column :good_jobs, :retried_good_job_id, :uuid
13
- end
14
- end