good_job 4.3.0 → 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: ec5d4c89f52824a8023774ca2179f67579c21ae7772737a367ee912bc58db9bd
4
- data.tar.gz: f8526687220d9bd8ec7222f897f2302ab7d42f8883627c43195390b7ab9cf0be
3
+ metadata.gz: f7e5a7c2df1683ddffde677bddf6bbd9d2d316ec646f571161749e4648ec7da7
4
+ data.tar.gz: dd2e664a3ada0f89e2a07731ef5d1ebff9c5edd87768ce584622f5ac33a0da12
5
5
  SHA512:
6
- metadata.gz: 3760a509091f29ca67d90366b06940cb4b12428bbe99a5fd451315ec9895291d2083168603f1f81a3b87ccf1a6df9bcf657d9c89a811768436fdd6a80aa60737
7
- data.tar.gz: 9ca52b976c7399f386e141c7ea6d54bfbf3b1b8036ef6f1ac37cc24dede46966ab7644c1c8a5bcde491c3a768862fd1edbd66b7054e06a0e6955e5f7b3d8245f
6
+ metadata.gz: 5faa526ba40039167498cf5c1046251feca2d0255746170de2f153531a5f973fdfc326319b16dcff30d893b38131fbb76d814cf4c4a15f5276b0f371bc05a3be
7
+ data.tar.gz: a8db86f639120ddc90b5de3245962a4630e006a26fc832b98dbb8892f9131298251780128ce03d7d3a94b59ec9b117d4fd277adc3e34fa96234c85c1378c9f8e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
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
+
3
28
  ## [v4.3.0](https://github.com/bensheldon/good_job/tree/v4.3.0) (2024-09-14)
4
29
 
5
30
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v4.2.1...v4.3.0)
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")`
@@ -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
@@ -478,7 +478,7 @@ module GoodJob
478
478
  current_thread.job = self
479
479
  current_thread.retry_now = true
480
480
 
481
- self.class.transaction(joinable: false, requires_new: true) do
481
+ transaction do
482
482
  new_active_job = active_job.retry_job(wait: 0, error: error)
483
483
  self.error_event = :retried if error
484
484
  save!
@@ -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
@@ -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
@@ -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.3.0'
5
+ VERSION = '4.4.0'
6
6
 
7
7
  # GoodJob version as Gem::Version object
8
8
  GEM_VERSION = Gem::Version.new(VERSION)
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.3.0
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-09-14 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