good_job 2.99.0 → 3.0.2

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: 2a19a18e7245448f7f44c99a492f4ca08f311cc96da9a50f4ba4d7b704685017
4
- data.tar.gz: 07330c1a31b873b172066d9e7946b0184b250f7276fe041617c378b35ce8beca
3
+ metadata.gz: 03dafffb596eaa6c7cf0994a40586039ac38215dcb0942850b1067deec0ac04d
4
+ data.tar.gz: 917cbb1baebf9e6f8f7767d54606d0a07b02d3c28971e65369603f71500abf34
5
5
  SHA512:
6
- metadata.gz: 7a38a4a1bc73cf7378f24850b1e58b9b7cf6fbf83ef7aff8fcdba7d2c77ce782e273a3bc946efcf21d98157523b68183bb36efcbd570d253eab73cc7e8542c99
7
- data.tar.gz: c97a8235d5b0041237909d6bb551bcccf2de5a4e2fa07bbbb12496a245aa6c4408896baa2a752b0a9f3eb6a1eb037189baa68e6d9979b57ef640e08750609d27
6
+ metadata.gz: fd0c36fee775613d86ba832c6777eb9c72b651630f24b6236ea1b5373957d045121c3d5a0aea528cded163da638b7b6e41c3b155915186350bd1675e27b3ffee
7
+ data.tar.gz: 041f30d08e8128d81ffdbf59e19f958761b809d9993facb5919119346cfc30b2f8da715658be47e94f317ffed3953381a93141a6bdc6540ab3f0b186604970f0
data/CHANGELOG.md CHANGED
@@ -1,5 +1,58 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.0.2](https://github.com/bensheldon/good_job/tree/v3.0.2) (2022-07-10)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.0.1...v3.0.2)
6
+
7
+ **Fixed bugs:**
8
+
9
+ - Copy forward concurrency key value when retrying a job, rather than regenerating it [\#622](https://github.com/bensheldon/good_job/issues/622)
10
+ - All concurrency controlled jobs throw exceptions and are rescheduled if they are called using perform\_now [\#591](https://github.com/bensheldon/good_job/issues/591)
11
+
12
+ **Closed issues:**
13
+
14
+ - Queue config not respecting limits [\#659](https://github.com/bensheldon/good_job/issues/659)
15
+ - UI engine does not work without explicit require [\#646](https://github.com/bensheldon/good_job/issues/646)
16
+ - Should `:inline` adapter mode retry jobs? [\#611](https://github.com/bensheldon/good_job/issues/611)
17
+ - Error Job Not Preserved [\#594](https://github.com/bensheldon/good_job/issues/594)
18
+ - Jobs never get run... [\#516](https://github.com/bensheldon/good_job/issues/516)
19
+ - Release GoodJob 3.0 [\#507](https://github.com/bensheldon/good_job/issues/507)
20
+ - Improve security of Gem releases [\#422](https://github.com/bensheldon/good_job/issues/422)
21
+
22
+ **Merged pull requests:**
23
+
24
+ - Preserve initial concurrency key when retrying jobs [\#657](https://github.com/bensheldon/good_job/pull/657) ([bensheldon](https://github.com/bensheldon))
25
+ - Add Dashboard troubleshooting note to explicitly require the engine [\#654](https://github.com/bensheldon/good_job/pull/654) ([bensheldon](https://github.com/bensheldon))
26
+ - Removes wrong parentheses [\#653](https://github.com/bensheldon/good_job/pull/653) ([esasse](https://github.com/esasse))
27
+
28
+ ## [v3.0.1](https://github.com/bensheldon/good_job/tree/v3.0.1) (2022-07-02)
29
+
30
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.0.0...v3.0.1)
31
+
32
+ **Closed issues:**
33
+
34
+ - ERROR: relation "good\_jobs" does not exist at character 454 [\#308](https://github.com/bensheldon/good_job/issues/308)
35
+
36
+ **Merged pull requests:**
37
+
38
+ - Fix `GoodJob.cleanup_preserved_jobs` to use `delete_all` instead of `destroy_all` [\#652](https://github.com/bensheldon/good_job/pull/652) ([bensheldon](https://github.com/bensheldon))
39
+ - Create codeql-analysis.yml [\#648](https://github.com/bensheldon/good_job/pull/648) ([bensheldon](https://github.com/bensheldon))
40
+
41
+ ## [v3.0.0](https://github.com/bensheldon/good_job/tree/v3.0.0) (2022-06-26)
42
+
43
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v2.99.0...v3.0.0)
44
+
45
+ **Implemented enhancements:**
46
+
47
+ - By default, preserve job records and automatically them clean up [\#545](https://github.com/bensheldon/good_job/pull/545) ([bensheldon](https://github.com/bensheldon))
48
+
49
+ **Merged pull requests:**
50
+
51
+ - Update tests to reflect default of `GoodJob.preserve_job_records = true`; update appraisal Gemfiles too [\#643](https://github.com/bensheldon/good_job/pull/643) ([bensheldon](https://github.com/bensheldon))
52
+ - Remove database migration shims and old migrations [\#642](https://github.com/bensheldon/good_job/pull/642) ([bensheldon](https://github.com/bensheldon))
53
+ - Remove support for EOL Rails 5.2 [\#637](https://github.com/bensheldon/good_job/pull/637) ([bensheldon](https://github.com/bensheldon))
54
+ - Remove/rename deprecated behavior and constants for GoodJob v3 [\#633](https://github.com/bensheldon/good_job/pull/633) ([bensheldon](https://github.com/bensheldon))
55
+
3
56
  ## [v2.99.0](https://github.com/bensheldon/good_job/tree/v2.99.0) (2022-06-26)
4
57
 
5
58
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v2.17.1...v2.99.0)
@@ -996,7 +1049,6 @@
996
1049
 
997
1050
  **Closed issues:**
998
1051
 
999
- - ERROR: relation "good\_jobs" does not exist at character 454 [\#308](https://github.com/bensheldon/good_job/issues/308)
1000
1052
  - Add Frozen String Literal to all files [\#298](https://github.com/bensheldon/good_job/issues/298)
1001
1053
  - Support for good\_job without Rails? [\#295](https://github.com/bensheldon/good_job/issues/295)
1002
1054
 
data/README.md CHANGED
@@ -144,9 +144,9 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
144
144
 
145
145
  ## Compatibility
146
146
 
147
- - **Ruby on Rails:** 5.2+
148
- - **Ruby:** MRI 2.5+. JRuby 9.2.13+
149
- - **Postgres:** 9.6+
147
+ - **Ruby on Rails:** 6.0+
148
+ - **Ruby:** Ruby 2.5+. JRuby 9.2.13+
149
+ - **Postgres:** 10.0+
150
150
 
151
151
  ## Configuration
152
152
 
@@ -280,7 +280,7 @@ Available configuration options are:
280
280
  - `cleanup_interval_seconds` (integer) Number of seconds a Scheduler will wait before cleaning up preserved jobs. Defaults to `nil`. Can also be set with the environment variable `GOOD_JOB_CLEANUP_INTERVAL_SECONDS`.
281
281
  - `inline_execution_respects_schedule` (boolean) Opt-in to future behavior of inline execution respecting scheduled jobs. Defaults to `false`.
282
282
  - `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`).
283
- - `preserve_job_records` (boolean) keeps job records in your database even after jobs are completed. (Default: `false`)
283
+ - `preserve_job_records` (boolean) keeps job records in your database even after jobs are completed. (Default: `true`)
284
284
  - `retry_on_unhandled_error` (boolean) causes jobs to be re-queued and retried if they raise an instance of `StandardError`. Be advised this may lead to jobs being repeated infinitely ([see below for more on retries](#retries)). Instances of `Exception`, like SIGINT, will *always* be retried, regardless of this attribute’s value. (Default: `true`)
285
285
  - `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:
286
286
 
@@ -311,8 +311,8 @@ Good Job’s general behavior can also be configured via attributes directly on
311
311
 
312
312
  - **`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._
313
313
  - **`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`.
314
- - **`GoodJob.preserve_job_records`** (boolean) keeps job records in your database even after jobs are completed. (Default: `false`)
315
- - **`GoodJob.retry_on_unhandled_error`** (boolean) causes jobs to be re-queued and retried if they raise an instance of `StandardError`. Be advised this may lead to jobs being repeated infinitely ([see below for more on retries](#retries)). Instances of `Exception`, like SIGINT, will *always* be retried, regardless of this attribute’s value. (Default: `true`)
314
+ - **`GoodJob.preserve_job_records`** (boolean) keeps job records in your database even after jobs are completed. (Default: `true`)
315
+ - **`GoodJob.retry_on_unhandled_error`** (boolean) causes jobs to be re-queued and retried if they raise an instance of `StandardError`. Be advised this may lead to jobs being repeated infinitely ([see below for more on retries](#retries)). Instances of `Exception`, like SIGINT, will *always* be retried, regardless of this attribute’s value. (Default: `false`)
316
316
  - **`GoodJob.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.
317
317
 
318
318
  You’ll generally want to configure these in `config/initializers/good_job.rb`, like so:
@@ -370,6 +370,16 @@ GoodJob includes a Dashboard as a mountable `Rails::Engine`.
370
370
 
371
371
  See more at [Monitor and preserve worked jobs](#monitor-and-preserve-worked-jobs)
372
372
 
373
+ **Troubleshooting the Dashboard:** Some applications are unable to autoload the Goodjob Engine. To work around this, explicitly require the Engine at the top of your `config/application.rb` file, immediately after Rails is required and before Bundler requires the Rails' groups.
374
+
375
+ ```ruby
376
+ # config/application.rb
377
+ require_relative 'boot'
378
+ require 'rails/all'
379
+ require 'good_job/engine' # <= Add this line
380
+ # ...
381
+ ```
382
+
373
383
  #### API-only Rails applications
374
384
 
375
385
  API-only Rails applications may not have all of the required Rack middleware for the GoodJob Dashboard to function. To re-add the middlware:
@@ -395,8 +405,6 @@ The Dashboard can be set to automatically refresh by checking "Live Poll" in the
395
405
 
396
406
  GoodJob can extend ActiveJob to provide limits on concurrently running jobs, either at time of _enqueue_ or at _perform_. Limiting concurrency can help prevent duplicate, double or unecessary jobs from being enqueued, or race conditions when performing, for example when interacting with 3rd-party APIs.
397
407
 
398
- **Note:** Limiting concurrency at _enqueue_ requires Rails 6.0+ because Rails 5.2 cannot halt ActiveJob callbacks.
399
-
400
408
  ```ruby
401
409
  class MyJob < ApplicationJob
402
410
  include GoodJob::ActiveJobExtensions::Concurrency
@@ -564,9 +572,9 @@ GoodJob.on_thread_error = -> (exception) { Raven.capture_exception(exception) }
564
572
 
565
573
  #### Retries
566
574
 
567
- By default, GoodJob will automatically and immediately retry a job when an exception is raised to GoodJob.
575
+ By default, GoodJob relies on ActiveJob's retry functionality.
568
576
 
569
- However, ActiveJob can be configured to retry an infinite number of times, with an exponential backoff. Using ActiveJob's `retry_on` prevents exceptions from reaching GoodJob:
577
+ ActiveJob can be configured to retry an infinite number of times, with an exponential backoff. Using ActiveJob's `retry_on` prevents exceptions from reaching GoodJob:
570
578
 
571
579
  ```ruby
572
580
  class ApplicationJob < ActiveJob::Base
@@ -575,14 +583,7 @@ class ApplicationJob < ActiveJob::Base
575
583
  end
576
584
  ```
577
585
 
578
- When using `retry_on` with _a limited number of retries_, the final exception will not be rescued and will raise to GoodJob. GoodJob can be configured to discard un-handled exceptions instead of retrying them. Be aware that if NOT setting `retry_on_unhandled_error` to `false` good_job will by default retry the failing job and may do this infinitely without pause thereby at least causing high load. In most cases `retry_on_unhandled_error` should be set as following:
579
-
580
- ```ruby
581
- # config/initializers/good_job.rb
582
- GoodJob.retry_on_unhandled_error = false
583
- ```
584
-
585
- Alternatively, pass a block to `retry_on` to handle the final exception instead of raising it to GoodJob:
586
+ When using `retry_on` with _a limited number of retries_, the final exception will not be rescued and will raise to GoodJob's error handler. To avoid this, pass a block to `retry_on` to handle the final exception instead of raising it to GoodJob:
586
587
 
587
588
  ```ruby
588
589
  class ApplicationJob < ActiveJob::Base
@@ -613,9 +614,11 @@ class ApplicationJob < ActiveJob::Base
613
614
  end
614
615
  ```
615
616
 
617
+ By default, jobs will not be retried unless `retry_on` is configured. This can be overridden by setting `GoodJob.retry_on_unhandled_error` to `true`; GoodJob will then retry the failing job immediately and infinitely, potentially causing high load.
618
+
616
619
  #### ActionMailer retries
617
620
 
618
- Any configuration in `ApplicationJob` will have to be duplicated on `ActionMailer::MailDeliveryJob` (`ActionMailer::DeliveryJob` in Rails 5.2 or earlier) because ActionMailer uses a custom class, `ActionMailer::MailDeliveryJob`, which inherits from `ActiveJob::Base`, rather than your applications `ApplicationJob`.
621
+ Any configuration in `ApplicationJob` will have to be duplicated on `ActionMailer::MailDeliveryJob` because ActionMailer uses that custom class which inherits from `ActiveJob::Base`, rather than your application's `ApplicationJob`.
619
622
 
620
623
  You can use an initializer to configure `ActionMailer::MailDeliveryJob`, for example:
621
624
 
@@ -720,7 +723,7 @@ Each GoodJob execution thread requires its own database connection that is autom
720
723
 
721
724
  ```yaml
722
725
  # config/database.yml
723
- pool: <%= ENV.fetch("RAILS_MAX_THREADS", 5).to_i + 3 + (ENV.fetch("GOOD_JOB_MAX_THREADS", 5).to_i %>
726
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS", 5).to_i + 3 + ENV.fetch("GOOD_JOB_MAX_THREADS", 5).to_i %>
724
727
  ```
725
728
 
726
729
  To calculate the total number of the database connections you'll need:
@@ -869,22 +872,31 @@ If your application is already using an ActiveJob backend, you will need to inst
869
872
 
870
873
  GoodJob is fully instrumented with [`ActiveSupport::Notifications`](https://edgeguides.rubyonrails.org/active_support_instrumentation.html#introduction-to-instrumentation).
871
874
 
872
- By default, GoodJob will destroy job records after they are run, regardless of whether they succeed or not (raising a kind of `StandardError`), unless they are interrupted (raising a kind of `Exception`).
875
+ By default, GoodJob will preserve job records for 14 days after they are run, regardless of whether they succeed or not (raising a kind of `StandardError`), unless they are interrupted (raising a kind of `Exception`).
873
876
 
874
- To preserve job records for later inspection, set an initializer:
877
+ To not preserve job records for later inspection, set an initializer:
875
878
 
876
879
  ```ruby
877
880
  # config/initializers/good_job.rb
878
- GoodJob.preserve_job_records = true
881
+ GoodJob.preserve_job_records = false # defaults to true, or `false` or `:on_unhandled_error`
882
+ ```
883
+
884
+ GoodJob will automatically delete these job records after 14 days. The retention period, as well as the frequency GoodJob checks for deletable records can be configured:
885
+
886
+ ```ruby
887
+
888
+ config.cleanup_preserved_jobs_before_seconds_ago = 14.days.to_i
889
+ config.cleanup_interval_jobs = 1_000 # Number of executed jobs between deletion sweeps.
890
+ config.cleanup_interval_seconds = 10.minutes.to_i # Number of seconds between deletion sweeps.
879
891
  ```
880
892
 
881
- It is also necessary to destroy these preserved jobs from the database after a certain time period:
893
+ It is also possible to manually trigger a cleanup:
882
894
 
883
895
  - For example, in a Rake task:
884
896
 
885
897
  ```ruby
886
- GoodJob.cleanup_preserved_jobs # Will keep 1 day of job records by default.
887
- GoodJob.cleanup_preserved_jobs(older_than: 7.days) # It also takes custom arguments.
898
+ GoodJob.cleanup_preserved_jobs # Will use default retention period
899
+ GoodJob.cleanup_preserved_jobs(older_than: 7.days) # custom retention period
888
900
  ```
889
901
 
890
902
  - For example, using the `good_job` command-line utility:
@@ -9,7 +9,7 @@ module GoodJob
9
9
  def data
10
10
  end_time = Time.current
11
11
  start_time = end_time - 1.day
12
- table_name = GoodJob::ActiveJobJob.table_name
12
+ table_name = GoodJob::Job.table_name
13
13
 
14
14
  count_query = Arel.sql(GoodJob::Execution.pg_or_jdbc_query(<<~SQL.squish))
15
15
  SELECT *
@@ -10,8 +10,8 @@ module GoodJob
10
10
  destroy: "destroyed",
11
11
  }.freeze
12
12
 
13
- rescue_from GoodJob::ActiveJobJob::AdapterNotGoodJobError,
14
- GoodJob::ActiveJobJob::ActionForStateMismatchError,
13
+ rescue_from GoodJob::Job::AdapterNotGoodJobError,
14
+ GoodJob::Job::ActionForStateMismatchError,
15
15
  with: :redirect_on_error
16
16
 
17
17
  def index
@@ -26,7 +26,7 @@ module GoodJob
26
26
  JobsFilter.new(params).filtered_query
27
27
  else
28
28
  job_ids = params.fetch(:job_ids, [])
29
- ActiveJobJob.where(active_job_id: job_ids)
29
+ Job.where(active_job_id: job_ids)
30
30
  end
31
31
 
32
32
  processed_jobs = jobs.map do |job|
@@ -42,7 +42,7 @@ module GoodJob
42
42
  end
43
43
 
44
44
  job
45
- rescue GoodJob::ActiveJobJob::ActionForStateMismatchError
45
+ rescue GoodJob::Job::ActionForStateMismatchError
46
46
  nil
47
47
  end.compact
48
48
 
@@ -56,29 +56,29 @@ module GoodJob
56
56
  end
57
57
 
58
58
  def show
59
- @job = ActiveJobJob.find(params[:id])
59
+ @job = Job.find(params[:id])
60
60
  end
61
61
 
62
62
  def discard
63
- @job = ActiveJobJob.find(params[:id])
63
+ @job = Job.find(params[:id])
64
64
  @job.discard_job(DISCARD_MESSAGE)
65
65
  redirect_back(fallback_location: jobs_path, notice: "Job has been discarded")
66
66
  end
67
67
 
68
68
  def reschedule
69
- @job = ActiveJobJob.find(params[:id])
69
+ @job = Job.find(params[:id])
70
70
  @job.reschedule_job
71
71
  redirect_back(fallback_location: jobs_path, notice: "Job has been rescheduled")
72
72
  end
73
73
 
74
74
  def retry
75
- @job = ActiveJobJob.find(params[:id])
75
+ @job = Job.find(params[:id])
76
76
  @job.retry_job
77
77
  redirect_back(fallback_location: jobs_path, notice: "Job has been retried")
78
78
  end
79
79
 
80
80
  def destroy
81
- @job = ActiveJobJob.find(params[:id])
81
+ @job = Job.find(params[:id])
82
82
  @job.destroy_job
83
83
  redirect_back(fallback_location: jobs_path, notice: "Job has been destroyed")
84
84
  end
@@ -87,9 +87,9 @@ module GoodJob
87
87
 
88
88
  def redirect_on_error(exception)
89
89
  alert = case exception
90
- when GoodJob::ActiveJobJob::AdapterNotGoodJobError
90
+ when GoodJob::Job::AdapterNotGoodJobError
91
91
  "ActiveJob Queue Adapter must be GoodJob."
92
- when GoodJob::ActiveJobJob::ActionForStateMismatchError
92
+ when GoodJob::Job::ActionForStateMismatchError
93
93
  "Job is not in an appropriate state for this action."
94
94
  else
95
95
  exception.to_s
@@ -2,7 +2,7 @@
2
2
  module GoodJob
3
3
  class ProcessesController < GoodJob::ApplicationController
4
4
  def index
5
- @processes = GoodJob::Process.active.order(created_at: :desc) if GoodJob::Process.migrated?
5
+ @processes = GoodJob::Process.active.order(created_at: :desc)
6
6
  end
7
7
  end
8
8
  end
@@ -31,7 +31,7 @@ module GoodJob
31
31
  when 'scheduled'
32
32
  query = query.scheduled
33
33
  when 'running'
34
- query = query.running.select("#{GoodJob::ActiveJobJob.table_name}.*", 'pg_locks.locktype')
34
+ query = query.running.select("#{GoodJob::Job.table_name}.*", 'pg_locks.locktype')
35
35
  when 'queued'
36
36
  query = query.queued
37
37
  end
@@ -47,7 +47,7 @@ module GoodJob
47
47
  private
48
48
 
49
49
  def default_base_query
50
- GoodJob::ActiveJobJob.all
50
+ GoodJob::Job.all
51
51
  end
52
52
  end
53
53
  end
@@ -3,15 +3,7 @@
3
3
  </div>
4
4
 
5
5
  <div data-live-poll-region="processes">
6
- <% if !GoodJob::Process.migrated? %>
7
- <div class="card my-3">
8
- <div class="card-body">
9
- <p class="card-text">
10
- <em>Feature unavailable because of pending database migration.</em>
11
- </p>
12
- </div>
13
- </div>
14
- <% elsif @processes.present? %>
6
+ <% if @processes.present? %>
15
7
  <div class="card my-3">
16
8
  <div class="table-responsive">
17
9
  <table class="table card-table table-bordered table-hover table-sm mb-0">
@@ -16,7 +16,7 @@
16
16
  <%= tag.script "", src: rails_ujs_path(format: :js, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
17
17
 
18
18
  <%= tag.script "", src: es_module_shims_path(format: :js, v: GoodJob::VERSION, locale: nil), async: true, nonce: content_security_policy_nonce %>
19
- <% importmaps = { imports: GoodJob::AssetsController.js_modules.keys.each_with_object({}) { |module_name, imports| imports[module_name] = modules_path(module_name, format: :js, locale: nil, v: GoodJob::VERSION) } } %>
19
+ <% importmaps = { imports: GoodJob::AssetsController.js_modules.keys.index_with { |module_name| modules_path(module_name, format: :js, locale: nil, v: GoodJob::VERSION) } } %>
20
20
  <%= tag.script importmaps.to_json.html_safe, type: "importmap", nonce: content_security_policy_nonce %>
21
21
  <%= tag.script "", src: scripts_path(format: :js, v: GoodJob::VERSION, locale: nil), type: "module", nonce: content_security_policy_nonce %>
22
22
  </head>
@@ -18,6 +18,12 @@ class CreateGoodJobs < ActiveRecord::Migration<%= migration_version %>
18
18
  t.text :concurrency_key
19
19
  t.text :cron_key
20
20
  t.uuid :retried_good_job_id
21
+ t.timestamp :cron_at
22
+ end
23
+
24
+ create_table :good_job_processes, id: :uuid do |t|
25
+ t.timestamps
26
+ t.jsonb :state
21
27
  end
22
28
 
23
29
  add_index :good_jobs, :scheduled_at, where: "(finished_at IS NULL)", name: "index_good_jobs_on_scheduled_at"
@@ -25,5 +31,8 @@ class CreateGoodJobs < ActiveRecord::Migration<%= migration_version %>
25
31
  add_index :good_jobs, [:active_job_id, :created_at], name: :index_good_jobs_on_active_job_id_and_created_at
26
32
  add_index :good_jobs, :concurrency_key, where: "(finished_at IS NULL)", name: :index_good_jobs_on_concurrency_key_when_unfinished
27
33
  add_index :good_jobs, [:cron_key, :created_at], name: :index_good_jobs_on_cron_key_and_created_at
34
+ add_index :good_jobs, [:cron_key, :cron_at], name: :index_good_jobs_on_cron_key_and_cron_at, unique: true
35
+ add_index :good_jobs, [:active_job_id], name: :index_good_jobs_on_active_job_id
36
+ add_index :good_jobs, [:finished_at], where: "retried_good_job_id IS NULL AND finished_at IS NOT NULL", name: :index_good_jobs_jobs_on_finished_at
28
37
  end
29
38
  end
@@ -10,8 +10,18 @@ module GoodJob
10
10
  end
11
11
  end
12
12
 
13
+ module Prepends
14
+ def deserialize(job_data)
15
+ super
16
+ self.good_job_concurrency_key = job_data['good_job_concurrency_key']
17
+ end
18
+ end
19
+
13
20
  included do
21
+ prepend Prepends
22
+
14
23
  class_attribute :good_job_concurrency_config, instance_accessor: false, default: {}
24
+ attr_writer :good_job_concurrency_key
15
25
 
16
26
  around_enqueue do |job, block|
17
27
  # Don't attempt to enforce concurrency limits with other queue adapters.
@@ -27,6 +37,8 @@ module GoodJob
27
37
  (total_limit.present? && (0...Float::INFINITY).cover?(total_limit))
28
38
  next(block.call) unless has_limit
29
39
 
40
+ # Only generate the concurrency key on the initial enqueue in case it is dynamic
41
+ job.good_job_concurrency_key ||= job._good_job_concurrency_key
30
42
  key = job.good_job_concurrency_key
31
43
  next(block.call) if key.blank?
32
44
 
@@ -80,7 +92,15 @@ module GoodJob
80
92
  end
81
93
  end
82
94
 
95
+ # Existing or dynamically generated concurrency key
96
+ # @return [Object] concurrency key
83
97
  def good_job_concurrency_key
98
+ @good_job_concurrency_key || _good_job_concurrency_key
99
+ end
100
+
101
+ # Generates the concurrency key from the configuration
102
+ # @return [Object] concurrency key
103
+ def _good_job_concurrency_key
84
104
  key = self.class.good_job_concurrency_config[:key]
85
105
  return if key.blank?
86
106
 
@@ -20,32 +20,16 @@ module GoodJob
20
20
  #
21
21
  # The default value depends on the Rails environment:
22
22
  #
23
- # - +development+ and +test+: +:inline+
23
+ # - +development+: +:async:+
24
+ # -+test+: +:inline+
24
25
  # - +production+ and all other environments: +:external+
25
26
  #
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+.
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 +"*"+.
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+.
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: nil)
31
- if queues || max_threads || poll_interval || start_async_on_initialize
32
- ActiveSupport::Deprecation.warn(
33
- "GoodJob::Adapter's execution-related arguments (queues, max_threads, poll_interval, start_async_on_initialize) have been deprecated and will be removed in GoodJob v3. These options should be configured through GoodJob global configuration instead."
34
- )
35
- end
36
-
37
- @configuration = GoodJob::Configuration.new(
38
- {
39
- execution_mode: execution_mode,
40
- queues: queues,
41
- max_threads: max_threads,
42
- poll_interval: poll_interval,
43
- }
44
- )
27
+ def initialize(execution_mode: nil)
28
+ @configuration = GoodJob::Configuration.new({ execution_mode: execution_mode })
45
29
  @configuration.validate!
46
30
  self.class.instances << self
47
31
 
48
- start_async if start_async_on_initialize || GoodJob.async_ready?
32
+ start_async if GoodJob.async_ready?
49
33
  end
50
34
 
51
35
  # Enqueues the ActiveJob job to be performed.
@@ -63,11 +47,7 @@ module GoodJob
63
47
  # @return [GoodJob::Execution]
64
48
  def enqueue_at(active_job, timestamp)
65
49
  scheduled_at = timestamp ? Time.zone.at(timestamp) : nil
66
-
67
- if execute_inline?
68
- future_scheduled = scheduled_at && scheduled_at > Time.current
69
- will_execute_inline = !future_scheduled || (future_scheduled && !@configuration.inline_execution_respects_schedule?)
70
- end
50
+ will_execute_inline = execute_inline? && (scheduled_at.nil? || scheduled_at <= Time.current)
71
51
 
72
52
  execution = GoodJob::Execution.enqueue(
73
53
  active_job,
@@ -76,29 +56,6 @@ module GoodJob
76
56
  )
77
57
 
78
58
  if will_execute_inline
79
- if future_scheduled && !@configuration.inline_execution_respects_schedule?
80
- ActiveSupport::Deprecation.warn(<<~DEPRECATION)
81
- In the next major release, GoodJob will not *inline* execute
82
- future-scheduled jobs.
83
-
84
- To opt into this behavior immediately set:
85
- `config.good_job.inline_execution_respects_schedule = true`
86
-
87
- To perform jobs inline at any time, use `GoodJob.perform_inline`.
88
-
89
- For example, using time helpers within an integration test:
90
-
91
- ```
92
- MyJob.set(wait: 10.minutes).perform_later
93
- travel_to(15.minutes.from_now) { GoodJob.perform_inline }
94
- ```
95
-
96
- Note: Rails `travel`/`travel_to` time helpers do not have millisecond
97
- precision, so you must leave at least 1 second between the schedule
98
- and time traveling for the job to be executed.
99
- DEPRECATION
100
- end
101
-
102
59
  begin
103
60
  result = execution.perform
104
61
  ensure
@@ -15,13 +15,13 @@ module GoodJob
15
15
  # Default poll interval for async in development environment
16
16
  DEFAULT_DEVELOPMENT_ASYNC_POLL_INTERVAL = -1
17
17
  # Default number of threads to use per {Scheduler}
18
- DEFAULT_MAX_CACHE = 10000
18
+ DEFAULT_MAX_CACHE = 10_000
19
19
  # Default number of seconds to preserve jobs for {CLI#cleanup_preserved_jobs} and {GoodJob.cleanup_preserved_jobs}
20
- DEFAULT_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO = 24 * 60 * 60
20
+ DEFAULT_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO = 14.days.to_i
21
21
  # Default number of jobs to execute between preserved job cleanup runs
22
- DEFAULT_CLEANUP_INTERVAL_JOBS = nil
22
+ DEFAULT_CLEANUP_INTERVAL_JOBS = 1_000
23
23
  # Default number of seconds to wait between preserved job cleanup runs
24
- DEFAULT_CLEANUP_INTERVAL_SECONDS = nil
24
+ DEFAULT_CLEANUP_INTERVAL_SECONDS = 10.minutes.to_i
25
25
  # Default to always wait for jobs to finish for {Adapter#shutdown}
26
26
  DEFAULT_SHUTDOWN_TIMEOUT = -1
27
27
  # Default to not running cron
@@ -56,8 +56,8 @@ module GoodJob
56
56
  # Exports values to hash
57
57
  # @return [Hash]
58
58
  def self.to_h
59
- ACCESSORS.each_with_object({}) do |accessor, hash|
60
- hash[accessor] = send(accessor)
59
+ ACCESSORS.index_with do |accessor|
60
+ send(accessor)
61
61
  end
62
62
  end
63
63
 
@@ -14,8 +14,6 @@ module GoodJob # :nodoc:
14
14
  # Registers the current process.
15
15
  def register_process
16
16
  GoodJob::Process.with_connection(connection) do
17
- next unless Process.migrated?
18
-
19
17
  GoodJob::Process.cleanup
20
18
  @process = GoodJob::Process.register
21
19
  end
@@ -24,8 +22,6 @@ module GoodJob # :nodoc:
24
22
  # Deregisters the current process.
25
23
  def deregister_process
26
24
  GoodJob::Process.with_connection(connection) do
27
- next unless Process.migrated?
28
-
29
25
  @process&.deregister
30
26
  end
31
27
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
3
  # GoodJob gem version.
4
- VERSION = '2.99.0'
4
+ VERSION = '3.0.2'
5
5
  end
data/lib/good_job.rb CHANGED
@@ -43,23 +43,21 @@ module GoodJob
43
43
 
44
44
  # @!attribute [rw] preserve_job_records
45
45
  # @!scope class
46
- # Whether to preserve job records in the database after they have finished (default: +false+).
47
- # By default, GoodJob destroys job records after the job is completed successfully.
46
+ # Whether to preserve job records in the database after they have finished (default: +true+).
47
+ # By default, GoodJob deletes job records after the job is completed successfully.
48
48
  # If you want to preserve jobs for latter inspection, set this to +true+.
49
49
  # If you want to preserve only jobs that finished with error for latter inspection, set this to +:on_unhandled_error+.
50
- # If +true+, you will need to clean out jobs using the +good_job cleanup_preserved_jobs+ CLI command or
51
- # by using +Goodjob.cleanup_preserved_jobs+.
52
50
  # @return [Boolean, nil]
53
- mattr_accessor :preserve_job_records, default: false
51
+ mattr_accessor :preserve_job_records, default: true
54
52
 
55
53
  # @!attribute [rw] retry_on_unhandled_error
56
54
  # @!scope class
57
- # Whether to re-perform a job when a type of +StandardError+ is raised to GoodJob (default: +true+).
55
+ # Whether to re-perform a job when a type of +StandardError+ is raised to GoodJob (default: +false+).
58
56
  # If +true+, causes jobs to be re-queued and retried if they raise an instance of +StandardError+.
59
57
  # If +false+, jobs will be discarded or marked as finished if they raise an instance of +StandardError+.
60
58
  # Instances of +Exception+, like +SIGINT+, will *always* be retried, regardless of this attribute's value.
61
59
  # @return [Boolean, nil]
62
- mattr_accessor :retry_on_unhandled_error, default: true
60
+ mattr_accessor :retry_on_unhandled_error, default: false
63
61
 
64
62
  # @!attribute [rw] on_thread_error
65
63
  # @!scope class
@@ -143,11 +141,11 @@ module GoodJob
143
141
  include_discarded = configuration.cleanup_discarded_jobs?
144
142
 
145
143
  ActiveSupport::Notifications.instrument("cleanup_preserved_jobs.good_job", { older_than: older_than, timestamp: timestamp }) do |payload|
146
- old_jobs = GoodJob::ActiveJobJob.where('finished_at <= ?', timestamp)
144
+ old_jobs = GoodJob::Job.where('finished_at <= ?', timestamp)
147
145
  old_jobs = old_jobs.not_discarded unless include_discarded
148
146
  old_jobs_count = old_jobs.count
149
147
 
150
- GoodJob::Execution.where(job: old_jobs).destroy_all
148
+ GoodJob::Execution.where(job: old_jobs).delete_all
151
149
  payload[:destroyed_records_count] = old_jobs_count
152
150
  end
153
151
  end