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 +4 -4
- data/CHANGELOG.md +45 -0
- data/README.md +15 -10
- data/app/controllers/good_job/jobs_controller.rb +6 -0
- data/app/models/good_job/batch.rb +10 -2
- data/app/models/good_job/batch_record.rb +21 -5
- data/app/models/good_job/cron_entry.rb +13 -0
- data/app/models/good_job/job.rb +3 -1
- data/app/models/good_job/setting.rb +8 -6
- data/config/routes.rb +1 -1
- data/lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb +1 -0
- data/lib/generators/good_job/templates/update/migrations/02_add_jobs_finished_at_to_good_job_batches.rb.erb +17 -0
- data/lib/good_job/active_job_extensions/labels.rb +1 -1
- data/lib/good_job/adapter.rb +1 -1
- data/lib/good_job/configuration.rb +6 -0
- data/lib/good_job/cron_manager.rb +22 -1
- data/lib/good_job/version.rb +1 -1
- data/lib/good_job.rb +4 -4
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7e5a7c2df1683ddffde677bddf6bbd9d2d316ec646f571161749e4648ec7da7
|
4
|
+
data.tar.gz: dd2e664a3ada0f89e2a07731ef5d1ebff9c5edd87768ce584622f5ac33a0da12
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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,
|
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 `
|
687
|
-
def perform(batch,
|
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
|
-
#
|
692
|
-
|
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 `
|
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,
|
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
|
-
#
|
772
|
-
|
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,
|
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
|
-
|
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
|
-
|
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(
|
56
|
-
|
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
|
-
|
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
|
data/app/models/good_job/job.rb
CHANGED
@@ -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
|
-
|
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?(
|
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?(
|
30
|
+
return unless disabled_setting&.value&.include?(key_string)
|
30
31
|
|
31
|
-
disabled_setting.value.delete(
|
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
|
-
|
38
|
-
|
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?(
|
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
@@ -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
|
data/lib/good_job/adapter.rb
CHANGED
@@ -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
|
data/lib/good_job/version.rb
CHANGED
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 [
|
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
|
-
|
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.
|
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
|
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
|