good_job 4.2.1 → 4.4.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: f21cb4322491fa83f330857033b5c1c0524509d57a93d795d3e4c7ce064e676c
4
- data.tar.gz: '05082cbbd0fd4a26587ac53a58d7042c546390505e68fb978ef675bdfe6a0fa6'
3
+ metadata.gz: f7e5a7c2df1683ddffde677bddf6bbd9d2d316ec646f571161749e4648ec7da7
4
+ data.tar.gz: dd2e664a3ada0f89e2a07731ef5d1ebff9c5edd87768ce584622f5ac33a0da12
5
5
  SHA512:
6
- metadata.gz: 825bed536a7ee957773981cc926f04dad400e0f2a770e5749a18511927b3c09a4130370a5ee0d88e984bf8ed3a09cce53bd0285bc1066c1e4a837835ee44ab21
7
- data.tar.gz: 04371b7bb7fb075dc5d11817ff942443609bfd33cd7112ba35c9617447ae520035d7fcfafff127e170b77dba4d258af5ebc54448c9d20c07cd1516d584076fdb
6
+ metadata.gz: 5faa526ba40039167498cf5c1046251feca2d0255746170de2f153531a5f973fdfc326319b16dcff30d893b38131fbb76d814cf4c4a15f5276b0f371bc05a3be
7
+ data.tar.gz: a8db86f639120ddc90b5de3245962a4630e006a26fc832b98dbb8892f9131298251780128ce03d7d3a94b59ec9b117d4fd277adc3e34fa96234c85c1378c9f8e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,50 @@
1
1
  # Changelog
2
2
 
3
+ ## [v4.4.0](https://github.com/bensheldon/good_job/tree/v4.4.0) (2024-10-08)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v4.3.0...v4.4.0)
6
+
7
+ **Fixed bugs:**
8
+
9
+ - fix: unable to use keyword args when including the `Labels` extension [\#1500](https://github.com/bensheldon/good_job/pull/1500) ([ElMassimo](https://github.com/ElMassimo))
10
+ - Fix GoodJob::Setting duplicate keys [\#1498](https://github.com/bensheldon/good_job/pull/1498) ([mohammednasser-32](https://github.com/mohammednasser-32))
11
+ - Allow `GoodJob::Job#retry_job`'s transaction to be joinable \(fix CI for current `rails-head`\) [\#1496](https://github.com/bensheldon/good_job/pull/1496) ([Earlopain](https://github.com/Earlopain))
12
+
13
+ **Closed issues:**
14
+
15
+ - Inline adapter doesn't retry jobs that throw a retryable error [\#1497](https://github.com/bensheldon/good_job/issues/1497)
16
+ - scheduled\_at not respecting given timezone [\#1491](https://github.com/bensheldon/good_job/issues/1491)
17
+ - Multiple same values in cron\_keys\_disabled, cron\_keys\_enabled in good\_job\_settings table [\#1490](https://github.com/bensheldon/good_job/issues/1490)
18
+ - Cron jobs can be missed if deploys are timed just right [\#1484](https://github.com/bensheldon/good_job/issues/1484)
19
+ - Configuring connection pool in external mode [\#1483](https://github.com/bensheldon/good_job/issues/1483)
20
+ - GoodJob Timezone Day Light Savings [\#1480](https://github.com/bensheldon/good_job/issues/1480)
21
+ - Unable to use labels with kwargs in job initializer [\#1350](https://github.com/bensheldon/good_job/issues/1350)
22
+
23
+ **Merged pull requests:**
24
+
25
+ - Bump puma from 6.4.2 to 6.4.3 [\#1492](https://github.com/bensheldon/good_job/pull/1492) ([dependabot[bot]](https://github.com/apps/dependabot))
26
+ - Add `cron_graceful_restart_period` to avoid missing recurring jobs that occurred during deployment downtime [\#1488](https://github.com/bensheldon/good_job/pull/1488) ([bensheldon](https://github.com/bensheldon))
27
+
28
+ ## [v4.3.0](https://github.com/bensheldon/good_job/tree/v4.3.0) (2024-09-14)
29
+
30
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v4.2.1...v4.3.0)
31
+
32
+ **Fixed bugs:**
33
+
34
+ - Don't shadow gem loading errors during autoloading with the inline adapter [\#1486](https://github.com/bensheldon/good_job/pull/1486) ([Earlopain](https://github.com/Earlopain))
35
+ - Ensure before actions run on root route [\#1482](https://github.com/bensheldon/good_job/pull/1482) ([ebiven](https://github.com/ebiven))
36
+
37
+ **Closed issues:**
38
+
39
+ - Undefined method `unhandled_error` for nil [\#1485](https://github.com/bensheldon/good_job/issues/1485)
40
+ - bin/rails g good\_job:install fails [\#1481](https://github.com/bensheldon/good_job/issues/1481)
41
+ - Rails 7.2.1 breaks enqueuing without enqueue\_after\_transaction\_commit? method on queue adapter [\#1477](https://github.com/bensheldon/good_job/issues/1477)
42
+
43
+ **Merged pull requests:**
44
+
45
+ - Fix documentation for 2nd Batch callback parameter: consistently call it `context` [\#1476](https://github.com/bensheldon/good_job/pull/1476) ([martijnversluis](https://github.com/martijnversluis))
46
+ - Redefine `Batch#finished_at` to mean all callback jobs have finished too; add `Batch#jobs_finished_at` to allow not deleting batches until all their callback jobs complete [\#1454](https://github.com/bensheldon/good_job/pull/1454) ([bensheldon](https://github.com/bensheldon))
47
+
3
48
  ## [v4.2.1](https://github.com/bensheldon/good_job/tree/v4.2.1) (2024-08-29)
4
49
 
5
50
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v4.2.0...v4.2.1)
data/README.md CHANGED
@@ -257,6 +257,7 @@ Rails.application.configure do
257
257
  config.good_job.shutdown_timeout = 25 # seconds
258
258
  config.good_job.enable_cron = true
259
259
  config.good_job.cron = { example: { cron: '0 * * * *', class: 'ExampleJob' } }
260
+ config.good_job.cron_graceful_restart_period = 5.minutes
260
261
  config.good_job.dashboard_default_locale = :en
261
262
 
262
263
  # ...or all at once.
@@ -298,6 +299,7 @@ Available configuration options are:
298
299
  - `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`.
299
300
  - `shutdown_timeout` (integer) number of seconds to wait for jobs to finish when shutting down before stopping the thread. Defaults to forever: `-1`. You can also set this with the environment variable `GOOD_JOB_SHUTDOWN_TIMEOUT`.
300
301
  - `enable_cron` (boolean) whether to run cron process. Defaults to `false`. You can also set this with the environment variable `GOOD_JOB_ENABLE_CRON`.
302
+ - `cron_graceful_restart_period` (integer) when restarting cron, attempt to re-enqueue jobs that would have been enqueued by cron within this time period (e.g. `1.minute`). This should match the expected downtime during deploys.
301
303
  - `enable_listen_notify` (boolean) whether to enqueue and read jobs with Postgres LISTEN/NOTIFY. Defaults to `true`. You can also set this with the environment variable `GOOD_JOB_ENABLE_LISTEN_NOTIFY`.
302
304
  - `cron` (hash) cron configuration. Defaults to `{}`. You can also set this as a JSON string with the environment variable `GOOD_JOB_CRON`
303
305
  - `cleanup_discarded_jobs` (boolean) whether to destroy discarded jobs when cleaning up preserved jobs using the `$ good_job cleanup_preserved_jobs` CLI command or calling `GoodJob.cleanup_preserved_jobs`. Defaults to `true`. Can also be set with the environment variable `GOOD_JOB_CLEANUP_DISCARDED_JOBS`. _This configuration is only used when {GoodJob.preserve_job_records} is `true`._
@@ -625,6 +627,9 @@ If you use the [Dashboard](#dashboard) the scheduled tasks can be viewed in the
625
627
  # Enable cron enqueuing in this process
626
628
  config.good_job.enable_cron = true
627
629
 
630
+ # Without zero-downtime deploys, re-attempt previous schedules after a deploy
631
+ config.good_job.cron_graceful_restart_period = 1.minute
632
+
628
633
  # Configure cron with a hash that has a unique key for each recurring job
629
634
  config.good_job.cron = {
630
635
  # Every 15 minutes, enqueue `ExampleJob.set(priority: -10).perform_later(42, "life", name: "Alice")`
@@ -681,15 +686,15 @@ Batches track a set of jobs, and enqueue an optional callback job when all of th
681
686
  OtherJob.perform_later
682
687
  end
683
688
 
684
- # When these jobs have finished, it will enqueue your `MyBatchCallbackJob.perform_later(batch, options)`
689
+ # When these jobs have finished, it will enqueue your `MyBatchCallbackJob.perform_later(batch, context)`
685
690
  class MyBatchCallbackJob < ApplicationJob
686
- # Callback jobs must accept a `batch` and `options` argument
687
- def perform(batch, params)
691
+ # Callback jobs must accept a `batch` and `context` argument
692
+ def perform(batch, context)
688
693
  # The batch object will contain the Batch's properties, which are mutable
689
694
  batch.properties[:user] # => <User id: 1, ...>
690
695
 
691
- # Params is a hash containing additional context (more may be added in the future)
692
- params[:event] # => :finish, :success, :discard
696
+ # Context is a hash containing additional context (more may be added in the future)
697
+ context[:event] # => :finish, :success, :discard
693
698
  end
694
699
  end
695
700
  ```
@@ -757,19 +762,19 @@ Batch callbacks are Active Job jobs that are enqueued at certain events during t
757
762
  - `:success` - Enqueued only when all jobs in the batch have finished and succeeded.
758
763
  - `:discard` - Enqueued immediately the first time a job in the batch is discarded.
759
764
 
760
- Callback jobs must accept a `batch` and `params` argument in their `perform` method:
765
+ Callback jobs must accept a `batch` and `context` argument in their `perform` method:
761
766
 
762
767
  ```ruby
763
768
  class MyBatchCallbackJob < ApplicationJob
764
- def perform(batch, params)
769
+ def perform(batch, context)
765
770
  # The batch object will contain the Batch's properties
766
771
  batch.properties[:user] # => <User id: 1, ...>
767
772
  # Batches are mutable
768
773
  batch.properties[:user] = User.find(2)
769
774
  batch.save
770
775
 
771
- # Params is a hash containing additional context (more may be added in the future)
772
- params[:event] # => :finish, :success, :discard
776
+ # Context is a hash containing additional context (more may be added in the future)
777
+ context[:event] # => :finish, :success, :discard
773
778
  end
774
779
  end
775
780
  ```
@@ -811,7 +816,7 @@ class WorkJob < ApplicationJob
811
816
  end
812
817
 
813
818
  class BatchJob < ApplicationJob
814
- def perform(batch, options)
819
+ def perform(batch, context)
815
820
  if batch.properties[:stage].nil?
816
821
  batch.enqueue(stage: 1) do
817
822
  WorkJob.perform_later('a')
@@ -94,6 +94,12 @@ module GoodJob
94
94
  redirect_to jobs_path, notice: t(".notice")
95
95
  end
96
96
 
97
+ def redirect_to_index
98
+ # Redirect to the jobs page, maintaining query parameters. This is
99
+ # necessary to support the `?poll=1` parameter that enables live polling.
100
+ redirect_to jobs_path(request.query_parameters)
101
+ end
102
+
97
103
  private
98
104
 
99
105
  def redirect_on_error(exception)
@@ -26,10 +26,12 @@ module GoodJob
26
26
  :enqueued_at,
27
27
  :finished_at,
28
28
  :discarded_at,
29
+ :jobs_finished_at,
29
30
  :enqueued?,
30
31
  :finished?,
31
32
  :succeeded?,
32
33
  :discarded?,
34
+ :jobs_finished?,
33
35
  :description,
34
36
  :description=,
35
37
  :on_finish,
@@ -95,8 +97,12 @@ module GoodJob
95
97
  record.transaction do
96
98
  record.with_advisory_lock(function: "pg_advisory_xact_lock") do
97
99
  record.enqueued_at_will_change!
100
+ record.jobs_finished_at_will_change! if GoodJob::BatchRecord.jobs_finished_at_migrated?
98
101
  record.finished_at_will_change!
99
- record.update!(enqueued_at: nil, finished_at: nil)
102
+
103
+ update_attributes = { discarded_at: nil, finished_at: nil }
104
+ update_attributes[:jobs_finished_at] = nil if GoodJob::BatchRecord.jobs_finished_at_migrated?
105
+ record.update!(**update_attributes)
100
106
  end
101
107
  end
102
108
  end
@@ -142,7 +148,9 @@ module GoodJob
142
148
  buffer = GoodJob::Adapter::InlineBuffer.capture do
143
149
  record.transaction do
144
150
  record.with_advisory_lock(function: "pg_advisory_xact_lock") do
145
- record.update!(discarded_at: nil, finished_at: nil)
151
+ update_attributes = { discarded_at: nil, finished_at: nil }
152
+ update_attributes[:jobs_finished_at] = nil if GoodJob::BatchRecord.jobs_finished_at_migrated?
153
+ record.update!(update_attributes)
146
154
  record.jobs.discarded.each(&:retry_job)
147
155
  record._continue_discard_or_finish(lock: false)
148
156
  end
@@ -38,6 +38,10 @@ module GoodJob
38
38
  query
39
39
  end)
40
40
 
41
+ def self.jobs_finished_at_migrated?
42
+ column_names.include?('jobs_finished_at')
43
+ end
44
+
41
45
  # Whether the batch has finished and no jobs were discarded
42
46
  # @return [Boolean]
43
47
  def succeeded?
@@ -52,22 +56,26 @@ module GoodJob
52
56
  attributes.except('serialized_properties').merge(properties: properties)
53
57
  end
54
58
 
55
- def _continue_discard_or_finish(execution = nil, lock: true)
56
- execution_discarded = execution && execution.finished_at.present? && execution.error.present?
59
+ def _continue_discard_or_finish(job = nil, lock: true)
60
+ job_discarded = job && job.finished_at.present? && job.error.present?
57
61
  buffer = GoodJob::Adapter::InlineBuffer.capture do
58
62
  advisory_lock_maybe(lock) do
59
63
  Batch.within_thread(batch_id: nil, batch_callback_id: id) do
60
64
  reload
61
- if execution_discarded && !discarded_at
65
+
66
+ if job_discarded && !discarded_at
62
67
  update(discarded_at: Time.current)
63
68
  on_discard.constantize.set(priority: callback_priority, queue: callback_queue_name).perform_later(to_batch, { event: :discard }) if on_discard.present?
64
69
  end
65
70
 
66
- if enqueued_at && !finished_at && jobs.where(finished_at: nil).count.zero?
67
- update(finished_at: Time.current)
71
+ if enqueued_at && !(self.class.jobs_finished_at_migrated? ? jobs_finished_at : finished_at) && jobs.where(finished_at: nil).count.zero?
72
+ self.class.jobs_finished_at_migrated? ? update(jobs_finished_at: Time.current) : update(finished_at: Time.current)
73
+
68
74
  on_success.constantize.set(priority: callback_priority, queue: callback_queue_name).perform_later(to_batch, { event: :success }) if !discarded_at && on_success.present?
69
75
  on_finish.constantize.set(priority: callback_priority, queue: callback_queue_name).perform_later(to_batch, { event: :finish }) if on_finish.present?
70
76
  end
77
+
78
+ update(finished_at: Time.current) if !finished_at && self.class.jobs_finished_at_migrated? && jobs_finished? && callback_jobs.where(finished_at: nil).count.zero?
71
79
  end
72
80
  end
73
81
  end
@@ -97,6 +105,14 @@ module GoodJob
97
105
  self.serialized_properties = value
98
106
  end
99
107
 
108
+ def jobs_finished?
109
+ self.class.jobs_finished_at_migrated? ? jobs_finished_at : finished_at
110
+ end
111
+
112
+ def jobs_finished_at
113
+ self.class.jobs_finished_at_migrated? ? self[:jobs_finished_at] : self[:finished_at]
114
+ end
115
+
100
116
  private
101
117
 
102
118
  def advisory_lock_maybe(value, &block)
@@ -71,6 +71,19 @@ module GoodJob # :nodoc:
71
71
  end
72
72
  end
73
73
 
74
+ def within(period, previously_at: nil)
75
+ if cron_proc?
76
+ result = Rails.application.executor.wrap { cron.call(previously_at || last_job_at) }
77
+ if result.is_a?(String)
78
+ Fugit.parse(result).within(period).map(&:to_t)
79
+ else
80
+ result
81
+ end
82
+ else
83
+ fugit.within(period).map(&:to_t)
84
+ end
85
+ end
86
+
74
87
  def enabled?
75
88
  GoodJob::Setting.cron_key_enabled?(key, default: enabled_by_default?)
76
89
  end
@@ -40,6 +40,7 @@ module GoodJob
40
40
  set_callback :perform_unlocked, :after, :continue_discard_or_finish_batch
41
41
 
42
42
  belongs_to :batch, class_name: 'GoodJob::BatchRecord', inverse_of: :jobs, optional: true
43
+ belongs_to :callback_batch, class_name: 'GoodJob::BatchRecord', foreign_key: :batch_callback_id, inverse_of: :callback_jobs, optional: true
43
44
  belongs_to :locked_by_process, class_name: "GoodJob::Process", foreign_key: :locked_by_id, inverse_of: :locked_jobs, optional: true
44
45
  has_many :executions, class_name: 'GoodJob::Execution', foreign_key: 'active_job_id', primary_key: "id", inverse_of: :job, dependent: :delete_all
45
46
 
@@ -477,7 +478,7 @@ module GoodJob
477
478
  current_thread.job = self
478
479
  current_thread.retry_now = true
479
480
 
480
- self.class.transaction(joinable: false, requires_new: true) do
481
+ transaction do
481
482
  new_active_job = active_job.retry_job(wait: 0, error: error)
482
483
  self.error_event = :retried if error
483
484
  save!
@@ -743,6 +744,7 @@ module GoodJob
743
744
 
744
745
  def continue_discard_or_finish_batch
745
746
  batch._continue_discard_or_finish(self) if batch.present?
747
+ callback_batch._continue_discard_or_finish if callback_batch.present?
746
748
  end
747
749
 
748
750
  def active_job_data
@@ -19,30 +19,32 @@ module GoodJob
19
19
  end
20
20
 
21
21
  def self.cron_key_enable(key)
22
+ key_string = key.to_s
22
23
  enabled_setting = find_or_initialize_by(key: CRON_KEYS_ENABLED) do |record|
23
24
  record.value = []
24
25
  end
25
- enabled_setting.value << key unless enabled_setting.value.include?(key)
26
+ enabled_setting.value << key unless enabled_setting.value.include?(key_string)
26
27
  enabled_setting.save!
27
28
 
28
29
  disabled_setting = GoodJob::Setting.find_by(key: CRON_KEYS_DISABLED)
29
- return unless disabled_setting&.value&.include?(key.to_s)
30
+ return unless disabled_setting&.value&.include?(key_string)
30
31
 
31
- disabled_setting.value.delete(key.to_s)
32
+ disabled_setting.value.delete(key_string)
32
33
  disabled_setting.save!
33
34
  end
34
35
 
35
36
  def self.cron_key_disable(key)
36
37
  enabled_setting = GoodJob::Setting.find_by(key: CRON_KEYS_ENABLED)
37
- if enabled_setting&.value&.include?(key.to_s)
38
- enabled_setting.value.delete(key.to_s)
38
+ key_string = key.to_s
39
+ if enabled_setting&.value&.include?(key_string)
40
+ enabled_setting.value.delete(key_string)
39
41
  enabled_setting.save!
40
42
  end
41
43
 
42
44
  disabled_setting = find_or_initialize_by(key: CRON_KEYS_DISABLED) do |record|
43
45
  record.value = []
44
46
  end
45
- disabled_setting.value << key unless disabled_setting.value.include?(key)
47
+ disabled_setting.value << key unless disabled_setting.value.include?(key_string)
46
48
  disabled_setting.save!
47
49
  end
48
50
  end
data/config/routes.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  GoodJob::Engine.routes.draw do
4
- root to: redirect(path: 'jobs')
4
+ root 'jobs#redirect_to_index'
5
5
 
6
6
  resources :jobs, only: %i[index show destroy] do
7
7
  collection do
@@ -46,6 +46,7 @@ class CreateGoodJobs < ActiveRecord::Migration<%= migration_version %>
46
46
  t.datetime :enqueued_at
47
47
  t.datetime :discarded_at
48
48
  t.datetime :finished_at
49
+ t.datetime :jobs_finished_at
49
50
  end
50
51
 
51
52
  create_table :good_job_executions, id: :uuid do |t|
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddJobsFinishedAtToGoodJobBatches < ActiveRecord::Migration<%= migration_version %>
4
+ def change
5
+ reversible do |dir|
6
+ dir.up do
7
+ # Ensure this incremental update migration is idempotent
8
+ # with monolithic install migration.
9
+ return if connection.column_exists?(:good_job_batches, :jobs_finished_at)
10
+ end
11
+ end
12
+
13
+ change_table :good_job_batches do |t|
14
+ t.datetime :jobs_finished_at
15
+ end
16
+ end
17
+ end
@@ -6,7 +6,7 @@ module GoodJob
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  module Prepends
9
- def initialize(*arguments)
9
+ def initialize(...)
10
10
  super
11
11
  self.good_job_labels = Array(self.class.good_job_labels.dup)
12
12
  end
@@ -254,7 +254,7 @@ module GoodJob
254
254
  job.advisory_unlock
255
255
  job.run_callbacks(:perform_unlocked)
256
256
 
257
- raise result.unhandled_error if result.unhandled_error
257
+ raise result.unhandled_error if result&.unhandled_error
258
258
  end
259
259
  end
260
260
  end
@@ -219,6 +219,12 @@ module GoodJob
219
219
  cron.map { |cron_key, params| GoodJob::CronEntry.new(params.merge(key: cron_key)) }
220
220
  end
221
221
 
222
+ def cron_graceful_restart_period
223
+ options[:cron_graceful_restart_period] ||
224
+ rails_config[:cron_graceful_restart_period] ||
225
+ env['GOOD_JOB_CRON_GRACEFUL_RESTART_PERIOD']
226
+ end
227
+
222
228
  # The number of queued jobs to select when polling for a job to run.
223
229
  # This limit is intended to avoid locking a large number of rows when selecting eligible jobs
224
230
  # from the queue. This value should be higher than the total number of threads across all good_job
@@ -31,11 +31,12 @@ module GoodJob # :nodoc:
31
31
 
32
32
  # @param cron_entries [Array<CronEntry>]
33
33
  # @param start_on_initialize [Boolean]
34
- def initialize(cron_entries = [], start_on_initialize: false, executor: Concurrent.global_io_executor)
34
+ def initialize(cron_entries = [], start_on_initialize: false, graceful_restart_period: nil, executor: Concurrent.global_io_executor)
35
35
  @executor = executor
36
36
  @running = false
37
37
  @cron_entries = cron_entries
38
38
  @tasks = Concurrent::Hash.new
39
+ @graceful_restart_period = graceful_restart_period
39
40
 
40
41
  start if start_on_initialize
41
42
  self.class.instances << self
@@ -47,6 +48,7 @@ module GoodJob # :nodoc:
47
48
  @running = true
48
49
  cron_entries.each do |cron_entry|
49
50
  create_task(cron_entry)
51
+ create_graceful_tasks(cron_entry) if @graceful_restart_period
50
52
  end
51
53
  end
52
54
  end
@@ -97,5 +99,24 @@ module GoodJob # :nodoc:
97
99
  future.add_observer(self.class, :task_observer)
98
100
  future.execute
99
101
  end
102
+
103
+ # Uses the graceful restart period to re-enqueue jobs that were scheduled to run during the period.
104
+ # The existing uniqueness logic should ensure this does not create duplicate jobs.
105
+ # @param cron_entry [CronEntry] the CronEntry object to schedule
106
+ def create_graceful_tasks(cron_entry)
107
+ return unless @graceful_restart_period
108
+
109
+ time_period = @graceful_restart_period.ago..Time.current
110
+ cron_entry.within(time_period).each do |cron_at|
111
+ future = Concurrent::Future.new(args: [self, cron_entry, cron_at], executor: @executor) do |_thr_scheduler, thr_cron_entry, thr_cron_at|
112
+ Rails.application.executor.wrap do
113
+ cron_entry.enqueue(thr_cron_at) if thr_cron_entry.enabled?
114
+ end
115
+ end
116
+
117
+ future.add_observer(self.class, :task_observer)
118
+ future.execute
119
+ end
120
+ end
100
121
  end
101
122
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module GoodJob
4
4
  # GoodJob gem version.
5
- VERSION = '4.2.1'
5
+ VERSION = '4.4.0'
6
6
 
7
7
  # GoodJob version as Gem::Version object
8
8
  GEM_VERSION = Gem::Version.new(VERSION)
data/lib/good_job.rb CHANGED
@@ -171,10 +171,10 @@ module GoodJob
171
171
  _shutdown_all(Capsule.instances, :restart, timeout: timeout)
172
172
  end
173
173
 
174
- # Sends +#shutdown+ or +#restart+ to executable objects ({GoodJob::Notifier}, {GoodJob::Poller}, {GoodJob::Scheduler}, {GoodJob::MultiScheduler}, {GoodJob::CronManager})
174
+ # Sends +#shutdown+ or +#restart+ to executable objects ({GoodJob::Notifier}, {GoodJob::Poller}, {GoodJob::Scheduler}, {GoodJob::MultiScheduler}, {GoodJob::CronManager}, {GoodJob::SharedExecutor})
175
175
  # @param executables [Array<Notifier, Poller, Scheduler, MultiScheduler, CronManager, SharedExecutor>] Objects to shut down.
176
- # @param method_name [:symbol] Method to call, e.g. +:shutdown+ or +:restart+.
177
- # @param timeout [nil,Numeric]
176
+ # @param method_name [Symbol] Method to call, e.g. +:shutdown+ or +:restart+.
177
+ # @param timeout [nil, Numeric] Seconds to wait for actively executing jobs to finish.
178
178
  # @param after [Array<Notifier, Poller, Scheduler, MultiScheduler, CronManager, SharedExecutor>] Objects to shut down after initial executables shut down.
179
179
  # @return [void]
180
180
  def self._shutdown_all(executables, method_name = :shutdown, timeout: -1, after: [])
@@ -290,7 +290,7 @@ module GoodJob
290
290
  # For use in tests/CI to validate GoodJob is up-to-date.
291
291
  # @return [Boolean]
292
292
  def self.migrated?
293
- true
293
+ GoodJob::BatchRecord.jobs_finished_at_migrated?
294
294
  end
295
295
  end
296
296
 
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: 4.2.1
4
+ version: 4.4.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: 2024-08-29 00:00:00.000000000 Z
11
+ date: 2024-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -340,6 +340,7 @@ files:
340
340
  - lib/generators/good_job/install_generator.rb
341
341
  - lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb
342
342
  - lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb.erb
343
+ - lib/generators/good_job/templates/update/migrations/02_add_jobs_finished_at_to_good_job_batches.rb.erb
343
344
  - lib/generators/good_job/update_generator.rb
344
345
  - lib/good_job.rb
345
346
  - lib/good_job/active_job_extensions/batches.rb