good_job 3.17.4 → 3.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e7b833bb3f1b28dca921562c3110e4147d955a98982f2b9481f9f7f2b41aa648
4
- data.tar.gz: 4f230602f1f5a2faf5198035f207e6f206bc614078a8bce8f38f71613250a8bd
3
+ metadata.gz: 2573770a9539b895705eee99e074ec34cc74a1b2fffc491c6b01fe2218792bf3
4
+ data.tar.gz: 49ae57e1ccfd0cf54421c349b1e0659dab195530f89ecce2bd6f29c37315e86d
5
5
  SHA512:
6
- metadata.gz: '0990adc9282acc9ad3b387f494b20e716e9f1f6ba37c1ec4da200dd89b53f8dd8d6cac60dc652323127086c837bf08bb02d3c28f1a75d589a1c435a897524b0d'
7
- data.tar.gz: 961e5e7e3d522f07572f9031096b940408f3bc7568a46b475d45daeb37ae755e237f497cdbb3221e4419bde4d73a560d743f132359b7ee03ffc8062d24f99b23
6
+ metadata.gz: aa1cdc0760d2d139acd4f76d95aab2093480930dd04b1a93673510497abb0f5d589ff1d71741d4e6f940d0c983bb003cbadeb06d9702a7b1165ba0f45bb13a47
7
+ data.tar.gz: 7184e6f9816441a85d2d2574482fbb36ea39a3c0f76bb9a5169df9ee7ad63e7a5f150f110087eba9938bfc87af79745c4a9427391906a04457ed1993b7c2f6f2
data/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.18.1](https://github.com/bensheldon/good_job/tree/v3.18.1) (2023-08-30)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.18.0...v3.18.1)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - Respect the configured execution mode, even within the CLI [\#1056](https://github.com/bensheldon/good_job/pull/1056) ([bensheldon](https://github.com/bensheldon))
10
+
11
+ **Closed issues:**
12
+
13
+ - Bug: calling `GoodJob.restart` in Puma on\_worker\_boot starts GoodJob regardless of it being in async mode or not [\#1054](https://github.com/bensheldon/good_job/issues/1054)
14
+ - \[Feature Request?\] Config Option to Inline Child-Jobs in Worker Processes [\#1052](https://github.com/bensheldon/good_job/issues/1052)
15
+ - \[Feature Request\] Hook to extend cron schedules [\#1050](https://github.com/bensheldon/good_job/issues/1050)
16
+
17
+ **Merged pull requests:**
18
+
19
+ - `GoodJob.restart` should not start capsules \(job execution\) when in a webserver but not in async mode [\#1055](https://github.com/bensheldon/good_job/pull/1055) ([bensheldon](https://github.com/bensheldon))
20
+
21
+ ## [v3.18.0](https://github.com/bensheldon/good_job/tree/v3.18.0) (2023-08-30)
22
+
23
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.17.4...v3.18.0)
24
+
25
+ **Implemented enhancements:**
26
+
27
+ - Support configuring cron schedule dynamically with a block [\#1051](https://github.com/bensheldon/good_job/pull/1051) ([DanielHeath](https://github.com/DanielHeath))
28
+
3
29
  ## [v3.17.4](https://github.com/bensheldon/good_job/tree/v3.17.4) (2023-08-24)
4
30
 
5
31
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.17.3...v3.17.4)
data/README.md CHANGED
@@ -504,6 +504,10 @@ config.good_job.cron = {
504
504
  cron: "0 0,12 * * *",
505
505
  class: "AnotherJob",
506
506
  },
507
+ complex_schedule: {
508
+ class: "ComplexScheduleJob",
509
+ cron: -> (last_ran) { (last_ran.blank? ? Time.now : last_ran + 14.hours).at_beginning_of_minute }
510
+ }
507
511
  # etc.
508
512
  }
509
513
  ```
@@ -27,6 +27,7 @@ module GoodJob # :nodoc:
27
27
  def initialize(params = {})
28
28
  @params = params
29
29
 
30
+ return if cron_proc?
30
31
  raise ArgumentError, "Invalid cron format: '#{cron}'" unless fugit.instance_of?(Fugit::Cron)
31
32
  end
32
33
 
@@ -41,10 +42,6 @@ module GoodJob # :nodoc:
41
42
  params.fetch(:class)
42
43
  end
43
44
 
44
- def cron
45
- params.fetch(:cron)
46
- end
47
-
48
45
  def set
49
46
  params[:set]
50
47
  end
@@ -61,16 +58,21 @@ module GoodJob # :nodoc:
61
58
  params[:description]
62
59
  end
63
60
 
64
- def next_at
61
+ def next_at(previously_at: nil)
62
+ if cron_proc?
63
+ result = Rails.application.executor.wrap { cron.call(previously_at || last_at) }
64
+ return Fugit.parse(result).next_time.to_t if result.is_a?(String)
65
+
66
+ return result
67
+
68
+ end
65
69
  fugit.next_time.to_t
66
70
  end
67
71
 
68
72
  def schedule
69
- fugit.original
70
- end
73
+ return "Custom schedule" if cron_proc?
71
74
 
72
- def fugit
73
- @_fugit ||= Fugit.parse(cron)
75
+ fugit.original
74
76
  end
75
77
 
76
78
  def jobs
@@ -80,11 +82,7 @@ module GoodJob # :nodoc:
80
82
  def last_at
81
83
  return if last_job.blank?
82
84
 
83
- if GoodJob::Job.column_names.include?('cron_at')
84
- (last_job.cron_at || last_job.created_at).localtime
85
- else
86
- last_job.created_at
87
- end
85
+ (last_job.cron_at || last_job.created_at).localtime
88
86
  end
89
87
 
90
88
  def enabled?
@@ -116,11 +114,7 @@ module GoodJob # :nodoc:
116
114
  end
117
115
 
118
116
  def last_job
119
- if GoodJob::Job.column_names.include?('cron_at')
120
- jobs.order("cron_at DESC NULLS LAST").first
121
- else
122
- jobs.order(created_at: :asc).last
123
- end
117
+ jobs.order("cron_at DESC NULLS LAST").first
124
118
  end
125
119
 
126
120
  def display_properties
@@ -138,6 +132,18 @@ module GoodJob # :nodoc:
138
132
 
139
133
  private
140
134
 
135
+ def cron
136
+ params.fetch(:cron)
137
+ end
138
+
139
+ def cron_proc?
140
+ cron.respond_to?(:call)
141
+ end
142
+
143
+ def fugit
144
+ @_fugit ||= Fugit.parse(cron)
145
+ end
146
+
141
147
  def set_value
142
148
  value = set || {}
143
149
  value.respond_to?(:call) ? value.call : value
@@ -185,7 +185,7 @@ module GoodJob
185
185
  # @return [Boolean]
186
186
  def execute_async?
187
187
  execution_mode == :async_all ||
188
- (execution_mode.in?([:async, :async_server]) && in_server_process?)
188
+ (execution_mode.in?([:async, :async_server]) && GoodJob.configuration.in_webserver?)
189
189
  end
190
190
 
191
191
  # Whether in +:external+ execution mode.
@@ -193,7 +193,7 @@ module GoodJob
193
193
  def execute_externally?
194
194
  execution_mode.nil? ||
195
195
  execution_mode == :external ||
196
- (execution_mode.in?([:async, :async_server]) && !in_server_process?)
196
+ (execution_mode.in?([:async, :async_server]) && !GoodJob.configuration.in_webserver?)
197
197
  end
198
198
 
199
199
  # Whether in +:inline+ execution mode.
@@ -219,21 +219,6 @@ module GoodJob
219
219
 
220
220
  private
221
221
 
222
- # Whether running in a web server process.
223
- # @return [Boolean, nil]
224
- def in_server_process?
225
- return @_in_server_process if defined? @_in_server_process
226
-
227
- @_in_server_process = Rails.const_defined?(:Server) || begin
228
- self_caller = caller
229
-
230
- self_caller.grep(%r{config.ru}).any? || # EXAMPLE: config.ru:3:in `block in <main>' OR config.ru:3:in `new_from_string'
231
- self_caller.grep(%r{puma/request}).any? || # EXAMPLE: puma-5.6.4/lib/puma/request.rb:76:in `handle_request'
232
- self_caller.grep(%{/rack/handler/}).any? || # EXAMPLE: iodine-0.7.44/lib/rack/handler/iodine.rb:13:in `start'
233
- (Concurrent.on_jruby? && self_caller.grep(%r{jruby/rack/rails_booter}).any?) # EXAMPLE: uri:classloader:/jruby/rack/rails_booter.rb:83:in `load_environment'
234
- end
235
- end
236
-
237
222
  def send_notify?(active_job)
238
223
  return false unless GoodJob.configuration.enable_listen_notify
239
224
  return true unless active_job.respond_to?(:good_job_notify)
@@ -79,6 +79,8 @@ module GoodJob
79
79
  def initialize(options, env: ENV)
80
80
  @options = options
81
81
  @env = env
82
+
83
+ @_in_webserver = nil
82
84
  end
83
85
 
84
86
  def validate!
@@ -89,21 +91,24 @@ module GoodJob
89
91
  # for more details on possible values.
90
92
  # @return [Symbol]
91
93
  def execution_mode
92
- mode = if GoodJob::CLI.within_exe?
93
- :external
94
- else
95
- options[:execution_mode] ||
96
- rails_config[:execution_mode] ||
97
- env['GOOD_JOB_EXECUTION_MODE']
98
- end
94
+ mode = options[:execution_mode] ||
95
+ rails_config[:execution_mode] ||
96
+ env['GOOD_JOB_EXECUTION_MODE']
97
+ mode = mode.to_sym if mode
99
98
 
100
99
  if mode
101
- mode.to_sym
100
+ if GoodJob::CLI.within_exe? && [:async, :async_server].include?(mode)
101
+ :external
102
+ else
103
+ mode
104
+ end
105
+ elsif GoodJob::CLI.within_exe?
106
+ :external
102
107
  elsif Rails.env.development?
103
108
  :async
104
109
  elsif Rails.env.test?
105
110
  :inline
106
- else
111
+ else # rubocop:disable Lint/DuplicateBranch
107
112
  :external
108
113
  end
109
114
  end
@@ -173,7 +178,7 @@ module GoodJob
173
178
 
174
179
  # The number of seconds to wait for jobs to finish when shutting down
175
180
  # before stopping the thread. +-1+ is forever.
176
- # @return [Numeric]
181
+ # @return [Float]
177
182
  def shutdown_timeout
178
183
  (
179
184
  options[:shutdown_timeout] ||
@@ -245,7 +250,7 @@ module GoodJob
245
250
 
246
251
  # Number of jobs a {Scheduler} will execute before automatically cleaning up preserved jobs.
247
252
  # Positive values will clean up after that many jobs have run, false or 0 will disable, and -1 will clean up after every job.
248
- # @return [Integer, nil]
253
+ # @return [Integer, Boolean, nil]
249
254
  def cleanup_interval_jobs
250
255
  if rails_config.key?(:cleanup_interval_jobs)
251
256
  value = rails_config[:cleanup_interval_jobs]
@@ -349,6 +354,20 @@ module GoodJob
349
354
  rails_config[:dashboard_default_locale] || DEFAULT_DASHBOARD_DEFAULT_LOCALE
350
355
  end
351
356
 
357
+ # Whether running in a web server process.
358
+ # @return [Boolean, nil]
359
+ def in_webserver?
360
+ return @_in_webserver unless @_in_webserver.nil?
361
+
362
+ @_in_webserver = Rails.const_defined?(:Server) || begin
363
+ self_caller = caller
364
+ self_caller.grep(%r{config.ru}).any? || # EXAMPLE: config.ru:3:in `block in <main>' OR config.ru:3:in `new_from_string'
365
+ self_caller.grep(%r{puma/request}).any? || # EXAMPLE: puma-5.6.4/lib/puma/request.rb:76:in `handle_request'
366
+ self_caller.grep(%{/rack/handler/}).any? || # EXAMPLE: iodine-0.7.44/lib/rack/handler/iodine.rb:13:in `start'
367
+ (Concurrent.on_jruby? && self_caller.grep(%r{jruby/rack/rails_booter}).any?) # EXAMPLE: uri:classloader:/jruby/rack/rails_booter.rb:83:in `load_environment'
368
+ end || false
369
+ end
370
+
352
371
  private
353
372
 
354
373
  def rails_config
@@ -82,12 +82,13 @@ module GoodJob # :nodoc:
82
82
 
83
83
  # Enqueues a scheduled task
84
84
  # @param cron_entry [CronEntry] the CronEntry object to schedule
85
- def create_task(cron_entry)
86
- cron_at = cron_entry.next_at
85
+ # @param previously_at [Date, Time, ActiveSupport::TimeWithZone, nil] the last, +in-memory+, scheduled time the cron task was intended to run
86
+ def create_task(cron_entry, previously_at: nil)
87
+ cron_at = cron_entry.next_at(previously_at: previously_at)
87
88
  delay = [(cron_at - Time.current).to_f, 0].max
88
89
  future = Concurrent::ScheduledTask.new(delay, args: [self, cron_entry, cron_at], executor: @executor) do |thr_scheduler, thr_cron_entry, thr_cron_at|
89
90
  # Re-schedule the next cron task before executing the current task
90
- thr_scheduler.create_task(thr_cron_entry)
91
+ thr_scheduler.create_task(thr_cron_entry, previously_at: thr_cron_at)
91
92
 
92
93
  Rails.application.executor.wrap do
93
94
  cron_entry.enqueue(thr_cron_at) if thr_cron_entry.enabled?
@@ -2,7 +2,7 @@
2
2
 
3
3
  module GoodJob
4
4
  # GoodJob gem version.
5
- VERSION = '3.17.4'
5
+ VERSION = '3.18.1'
6
6
 
7
7
  # GoodJob version as Gem::Version object
8
8
  GEM_VERSION = Gem::Version.new(VERSION)
data/lib/good_job.rb CHANGED
@@ -151,6 +151,8 @@ module GoodJob
151
151
  # @param timeout [Numeric] Seconds to wait for active threads to finish.
152
152
  # @return [void]
153
153
  def self.restart(timeout: -1)
154
+ return if configuration.execution_mode != :async && configuration.in_webserver?
155
+
154
156
  _shutdown_all(Capsule.instances, :restart, timeout: timeout)
155
157
  end
156
158
 
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: 3.17.4
4
+ version: 3.18.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Sheldon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-08-24 00:00:00.000000000 Z
11
+ date: 2023-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob