good_job 3.17.4 → 3.18.1

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: 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