good_job 1.11.3 → 1.12.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: 5e0f0ca8d7f8b024f5e84b515b8603d362b6c755d54680221e517369b6877c07
4
- data.tar.gz: e69518c9d71d7b08e3707c9df8c6429ca4d9823fd1adea32adc6c0bf41ad33f4
3
+ metadata.gz: bdf4b1eebb1224acd53f07753d5cda9cb9fde7a44baf300824d7816da94980f2
4
+ data.tar.gz: 4b90c9cd8133f179f87094683218ed1a1aed3582512169dd914a00dd62a81628
5
5
  SHA512:
6
- metadata.gz: 50d2542bfad082dfba516c08bd994dad3fcd6e8311a8380dc012988c3e2745378271adc7d41cb91897d087a6b2b100cfb69362d3a11cd55c92c2610ceabeb80c
7
- data.tar.gz: 69ffc79dc9367fc8522bdcf669cede6791d4e7903847edbc99887404b29ec43361de54a960841ee6cdeaaf236cc77d8c9c60b2c61793b5256bf2455667e0c387
6
+ metadata.gz: e95350cb8bd69198231d8030cc07adb9c87649499d30b6aa467ad4a06244920f2e950f46eae891e6963532fa879000959fcaa2eddf6093b8cd9d960eefef9abb
7
+ data.tar.gz: 1e09cf2851ca64fc34407e29f5afbac5bd7a72f072cef7c22798458b9ebc5678e287abbb03ee0d070cea8aa0665d85b017b94b68737e3e26ce7c0f67905c66e0
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [v1.12.0](https://github.com/bensheldon/good_job/tree/v1.12.0) (2021-07-27)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v1.11.3...v1.12.0)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - Add the ability to schedule repeating / recurring / cron-like jobs [\#53](https://github.com/bensheldon/good_job/issues/53)
10
+ - Add cron-like support for recurring/repeating jobs [\#297](https://github.com/bensheldon/good_job/pull/297) ([bensheldon](https://github.com/bensheldon))
11
+
12
+ **Merged pull requests:**
13
+
14
+ - Place Dashboard shared view partials under `good_job` namespace [\#310](https://github.com/bensheldon/good_job/pull/310) ([bensheldon](https://github.com/bensheldon))
15
+ - Ensure Dashboard inline javascript has CSP nonce for strict Content-Security Policy [\#309](https://github.com/bensheldon/good_job/pull/309) ([bensheldon](https://github.com/bensheldon))
16
+
3
17
  ## [v1.11.3](https://github.com/bensheldon/good_job/tree/v1.11.3) (2021-07-25)
4
18
 
5
19
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v1.11.2...v1.11.3)
data/README.md CHANGED
@@ -38,7 +38,8 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
38
38
  - [Configuration options](#configuration-options)
39
39
  - [Global options](#global-options)
40
40
  - [Dashboard](#dashboard)
41
- - [ActiveJob Concurrency](#activejob-concurrency)
41
+ - [ActiveJob concurrency](#activejob-concurrency)
42
+ - [Cron-style repeating/recurring jobs](#cron-style-repeatingrecurring-jobs)
42
43
  - [Updating](#updating)
43
44
  - [Go deeper](#go-deeper)
44
45
  - [Exceptions, retries, and reliability](#exceptions-retries-and-reliability)
@@ -156,6 +157,7 @@ Options:
156
157
  [--poll-interval=SECONDS] # Interval between polls for available jobs in seconds (env var: GOOD_JOB_POLL_INTERVAL, default: 1)
157
158
  [--max-cache=COUNT] # Maximum number of scheduled jobs to cache in memory (env var: GOOD_JOB_MAX_CACHE, default: 10000)
158
159
  [--shutdown-timeout=SECONDS] # Number of seconds to wait for jobs to finish when shutting down before stopping the thread. (env var: GOOD_JOB_SHUTDOWN_TIMEOUT, default: -1 (forever))
160
+ [--enable-cron] # Whether to run cron process (default: false)
159
161
  [--daemonize] # Run as a background daemon (default: false)
160
162
  [--pidfile=PIDFILE] # Path to write daemonized Process ID (env var: GOOD_JOB_PIDFILE, default: tmp/pids/good_job.pid)
161
163
 
@@ -212,7 +214,8 @@ config.good_job.execution_mode = :async_server
212
214
  config.good_job.max_threads = 5
213
215
  config.good_job.poll_interval = 30 # seconds
214
216
  config.good_job.shutdown_timeout = 25 # seconds
215
-
217
+ config.good_job.enable_cron = true
218
+ config.good_job.cron = { example: { cron: '0 * * * *', class: 'ExampleJob' } }
216
219
 
217
220
  # ...or all at once.
218
221
  config.good_job = {
@@ -220,6 +223,13 @@ config.good_job = {
220
223
  max_threads: 5,
221
224
  poll_interval: 30,
222
225
  shutdown_timeout: 25,
226
+ enable_cron: true,
227
+ cron: {
228
+ example: {
229
+ cron: '0 * * * *',
230
+ class: 'ExampleJob'
231
+ },
232
+ },
223
233
  }
224
234
  ```
225
235
 
@@ -235,6 +245,8 @@ Available configuration options are:
235
245
  - `poll_interval` (integer) sets the number of seconds between polls for jobs when `execution_mode` is set to `:async` or `:async_server`. You can also set this with the environment variable `GOOD_JOB_POLL_INTERVAL`.
236
246
  - `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`.
237
247
  - `shutdown_timeout` (float) 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`.
248
+ - `enable_cron` (boolean) whether to run cron process. Defaults to `false`. You can also set this with the environment variable `GOOD_JOB_ENABLE_CRON`.
249
+ - `cron` (hash) cron configuration. Defaults to `{}`. You can also set this as a JSON string with the environment variable `GOOD_JOB_CRON`
238
250
 
239
251
  By default, GoodJob configures the following execution modes per environment:
240
252
 
@@ -320,7 +332,7 @@ GoodJob includes a Dashboard as a mountable `Rails::Engine`.
320
332
  end
321
333
  ```
322
334
 
323
- ### ActiveJob Concurrency
335
+ ### ActiveJob concurrency
324
336
 
325
337
  GoodJob can extend ActiveJob to provide limits on concurrently running jobs, either at time of _enqueue_ or at _perform_.
326
338
 
@@ -349,6 +361,38 @@ class MyJob < ApplicationJob
349
361
  end
350
362
  ```
351
363
 
364
+ ### Cron-style repeating/recurring jobs
365
+
366
+ GoodJob can enqueue jobs on a recurring basis that can be used as a replacement for cron.
367
+
368
+ Cron-style jobs are run on every GoodJob process (e.g. CLI or `async` execution mode) when `config.good_job.enable_cron = true`; use GoodJob's [ActiveJob concurrency](#activejob-concurrency) extension to limit the number of jobs that are enqueued.
369
+
370
+ Cron-format is parsed by the [`fugit`](https://github.com/floraison/fugit) gem, which has support for seconds-level resolution (e.g. `* * * * * *`).
371
+
372
+ ```ruby
373
+ # config/environments/application.rb or a specific environment e.g. production.rb
374
+
375
+ # Enable cron in this process; e.g. only run on the first Heroku worker process
376
+ config.good_job.enable_cron = ENV['DYNO'] == 'worker.1' # or `true` or via $GOOD_JOB_ENABLE_CRON
377
+
378
+ # Configure cron with a hash that has a unique key for each recurring job
379
+ config.good_job.cron = {
380
+ # Every 15 minutes, enqueue `ExampleJob.set(priority: -10).perform_later(52, name: "Alice")`
381
+ frequent_task: { # each recurring job must have a unique key
382
+ cron: "*/15 * * * *", # cron-style scheduling format by fugit gem
383
+ class: "ExampleJob", # reference the Job class with a string
384
+ args: [42, { name: "Alice" }], # arguments to pass; can also be a proc e.g. `-> { { when: Time.now } }`
385
+ set: { priority: -10 }, # additional ActiveJob properties; can also be a lambda/proc e.g. `-> { { priority: [1,2].sample } }`
386
+ description: "Something helpful", # optional description that appears in Dashboard (coming soon!)
387
+ },
388
+ another_task: {
389
+ cron: "0 0,12 * * *",
390
+ class: "AnotherJob",
391
+ },
392
+ # etc.
393
+ }
394
+ ```
395
+
352
396
  ### Updating
353
397
 
354
398
  GoodJob follows semantic versioning, though updates may be encouraged through deprecation warnings in minor versions.
@@ -1 +1 @@
1
- <%= render 'shared/jobs_table', jobs: @jobs %>
1
+ <%= render 'good_job/shared/jobs_table', jobs: @jobs %>
@@ -1,5 +1,5 @@
1
1
  <div class="card my-3 p-6">
2
- <%= render 'shared/chart', chart_data: @chart %>
2
+ <%= render 'good_job/shared/chart', chart_data: @chart %>
3
3
  </div>
4
4
 
5
5
  <div class='card mb-2'>
@@ -38,7 +38,7 @@
38
38
  </div>
39
39
 
40
40
  <% if @filter.jobs.present? %>
41
- <%= render 'shared/jobs_table', jobs: @filter.jobs %>
41
+ <%= render 'good_job/shared/jobs_table', jobs: @filter.jobs %>
42
42
 
43
43
  <nav aria-label="Job pagination" class="mt-3">
44
44
  <ul class="pagination">
@@ -1,6 +1,6 @@
1
1
  <div id="chart"></div>
2
2
 
3
- <script>
3
+ <%= javascript_tag nonce: true do %>
4
4
  new Chartist.Line('#chart', <%== chart_data.to_json %>, {
5
5
  height: '300px',
6
6
  fullWidth: true,
@@ -49,4 +49,4 @@
49
49
  tooltipEl.style.left = (event.offsetX || event.originalEvent.layerX) + tooltipEl.offsetWidth + 10 + 'px';
50
50
  tooltipEl.style.top = (event.offsetY || event.originalEvent.layerY) + tooltipEl.offsetHeight - 20 + 'px';
51
51
  }, true);
52
- </script>
52
+ <% end %>
@@ -29,7 +29,7 @@
29
29
  </td>
30
30
  <td>
31
31
  <%= button_to job_path(job.id), method: :delete, class: "btn btn-sm btn-outline-danger", title: "Delete job" do %>
32
- <%= render "shared/icons/trash" %>
32
+ <%= render "good_job/shared/icons/trash" %>
33
33
  <% end %>
34
34
  </td>
35
35
  </tr>
@@ -53,13 +53,13 @@
53
53
 
54
54
  <% if notice %>
55
55
  <div class="alert alert-success alert-dismissible fade show d-flex align-items-center offset-md-3 col-6" role="alert">
56
- <%= render "shared/icons/check", class: "flex-shrink-0 me-2" %>
56
+ <%= render "good_job/shared/icons/check", class: "flex-shrink-0 me-2" %>
57
57
  <div><%= notice %></div>
58
58
  <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
59
59
  </div>
60
60
  <% elsif alert %>
61
61
  <div class="alert alert-warning alert-dismissible fade show d-flex align-items-center offset-md-3 col-6" role="alert">
62
- <%= render "shared/icons/check", class: "flex-shrink-0 me-2" %>
62
+ <%= render "good_job/shared/icons/check", class: "flex-shrink-0 me-2" %>
63
63
  <div><%= alert %></div>
64
64
  <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
65
65
  </div>
data/lib/good_job.rb CHANGED
@@ -106,16 +106,13 @@ module GoodJob
106
106
  wait ? -1 : nil
107
107
  end
108
108
 
109
- executables = Array(Notifier.instances) + Array(Poller.instances) + Array(Scheduler.instances)
110
- _shutdown_all(executables, timeout: timeout)
109
+ _shutdown_all(_executables, timeout: timeout)
111
110
  end
112
111
 
113
112
  # Tests whether jobs have stopped executing.
114
113
  # @return [Boolean] whether background threads are shut down
115
114
  def self.shutdown?
116
- Notifier.instances.all?(&:shutdown?) &&
117
- Poller.instances.all?(&:shutdown?) &&
118
- Scheduler.instances.all?(&:shutdown?)
115
+ _executables.all?(&:shutdown?)
119
116
  end
120
117
 
121
118
  # Stops and restarts executing jobs.
@@ -126,8 +123,7 @@ module GoodJob
126
123
  # @param timeout [Numeric, nil] Seconds to wait for active threads to finish.
127
124
  # @return [void]
128
125
  def self.restart(timeout: -1)
129
- executables = Array(Notifier.instances) + Array(Poller.instances) + Array(Scheduler.instances)
130
- _shutdown_all(executables, :restart, timeout: timeout)
126
+ _shutdown_all(_executables, :restart, timeout: timeout)
131
127
  end
132
128
 
133
129
  # Sends +#shutdown+ or +#restart+ to executable objects ({GoodJob::Notifier}, {GoodJob::Poller}, {GoodJob::Scheduler})
@@ -146,5 +142,14 @@ module GoodJob
146
142
  end
147
143
  end
148
144
 
145
+ def self._executables
146
+ [].concat(
147
+ CronManager.instances,
148
+ Notifier.instances,
149
+ Poller.instances,
150
+ Scheduler.instances
151
+ )
152
+ end
153
+
149
154
  ActiveSupport.run_load_hooks(:good_job, self)
150
155
  end
@@ -57,6 +57,8 @@ module GoodJob
57
57
  @scheduler = GoodJob::Scheduler.from_configuration(@configuration, warm_cache_on_initialize: Rails.application.initialized?)
58
58
  @notifier.recipients << [@scheduler, :create_thread]
59
59
  @poller.recipients << [@scheduler, :create_thread]
60
+
61
+ @cron_manager = GoodJob::CronManager.new(@configuration.cron, start_on_initialize: Rails.application.initialized?) if @configuration.enable_cron?
60
62
  end
61
63
  end
62
64
 
data/lib/good_job/cli.rb CHANGED
@@ -70,12 +70,16 @@ module GoodJob
70
70
  type: :numeric,
71
71
  banner: 'SECONDS',
72
72
  desc: "Number of seconds to wait for jobs to finish when shutting down before stopping the thread. (env var: GOOD_JOB_SHUTDOWN_TIMEOUT, default: -1 (forever))"
73
+ method_option :enable_cron,
74
+ type: :boolean,
75
+ desc: "Whether to run cron process (default: false)"
73
76
  method_option :daemonize,
74
77
  type: :boolean,
75
78
  desc: "Run as a background daemon (default: false)"
76
79
  method_option :pidfile,
77
80
  type: :string,
78
81
  desc: "Path to write daemonized Process ID (env var: GOOD_JOB_PIDFILE, default: tmp/pids/good_job.pid)"
82
+
79
83
  def start
80
84
  set_up_application!
81
85
  configuration = GoodJob::Configuration.new(options)
@@ -87,7 +91,7 @@ module GoodJob
87
91
  scheduler = GoodJob::Scheduler.from_configuration(configuration, warm_cache_on_initialize: true)
88
92
  notifier.recipients << [scheduler, :create_thread]
89
93
  poller.recipients << [scheduler, :create_thread]
90
-
94
+ cron_manager = GoodJob::CronManager.new(configuration.cron, start_on_initialize: true) if configuration.enable_cron?
91
95
  @stop_good_job_executable = false
92
96
  %w[INT TERM].each do |signal|
93
97
  trap(signal) { @stop_good_job_executable = true }
@@ -98,7 +102,7 @@ module GoodJob
98
102
  break if @stop_good_job_executable || scheduler.shutdown? || notifier.shutdown?
99
103
  end
100
104
 
101
- executors = [notifier, poller, scheduler]
105
+ executors = [notifier, poller, cron_manager, scheduler].compact
102
106
  GoodJob._shutdown_all(executors, timeout: configuration.shutdown_timeout)
103
107
  end
104
108
 
@@ -124,6 +128,7 @@ module GoodJob
124
128
  type: :numeric,
125
129
  banner: 'SECONDS',
126
130
  desc: "Delete records finished more than this many seconds ago (env var: GOOD_JOB_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO, default: 86400)"
131
+
127
132
  def cleanup_preserved_jobs
128
133
  set_up_application!
129
134
 
@@ -18,6 +18,8 @@ module GoodJob
18
18
  DEFAULT_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO = 24 * 60 * 60
19
19
  # Default to always wait for jobs to finish for {Adapter#shutdown}
20
20
  DEFAULT_SHUTDOWN_TIMEOUT = -1
21
+ # Default to not running cron
22
+ DEFAULT_ENABLE_CRON = false
21
23
 
22
24
  # The options that were explicitly set when initializing +Configuration+.
23
25
  # @return [Hash]
@@ -129,6 +131,28 @@ module GoodJob
129
131
  ).to_f
130
132
  end
131
133
 
134
+ # Whether to run cron
135
+ # @return [Boolean]
136
+ def enable_cron
137
+ value = ActiveModel::Type::Boolean.new.cast(
138
+ options[:enable_cron] ||
139
+ rails_config[:enable_cron] ||
140
+ env['GOOD_JOB_ENABLE_CRON'] ||
141
+ false
142
+ )
143
+ value && cron.size.positive?
144
+ end
145
+ alias enable_cron? enable_cron
146
+
147
+ def cron
148
+ env_cron = JSON.parse(ENV['GOOD_JOB_CRON']) if ENV['GOOD_JOB_CRON'].present?
149
+
150
+ options[:cron] ||
151
+ rails_config[:cron] ||
152
+ env_cron ||
153
+ {}
154
+ end
155
+
132
156
  # Number of seconds to preserve jobs when using the +good_job cleanup_preserved_jobs+ CLI command.
133
157
  # This configuration is only used when {GoodJob.preserve_job_records} is +true+.
134
158
  # @return [Integer]
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+ require "concurrent/hash"
3
+ require "concurrent/scheduled_task"
4
+ require "fugit"
5
+
6
+ module GoodJob # :nodoc:
7
+ #
8
+ # CronManagers enqueue jobs on a repeating schedule.
9
+ #
10
+ class CronManager
11
+ # @!attribute [r] instances
12
+ # @!scope class
13
+ # List of all instantiated CronManagers in the current process.
14
+ # @return [Array<GoodJob::CronManagers>, nil]
15
+ cattr_reader :instances, default: [], instance_reader: false
16
+
17
+ # Task observer for cron task
18
+ # @param time [Time]
19
+ # @param output [Object]
20
+ # @param thread_error [Exception]
21
+ def self.task_observer(time, output, thread_error) # rubocop:disable Lint/UnusedMethodArgument
22
+ return if thread_error.is_a? Concurrent::CancelledOperationError
23
+
24
+ GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
25
+ end
26
+
27
+ # Job configuration to be scheduled
28
+ # @return [Hash]
29
+ attr_reader :schedules
30
+
31
+ # @param schedules [Hash]
32
+ # @param start_on_initialize [Boolean]
33
+ def initialize(schedules = {}, start_on_initialize: false)
34
+ @running = false
35
+ @schedules = schedules
36
+ @tasks = Concurrent::Hash.new
37
+
38
+ self.class.instances << self
39
+
40
+ start if start_on_initialize
41
+ end
42
+
43
+ # Schedule tasks that will enqueue jobs based on their schedule
44
+ def start
45
+ ActiveSupport::Notifications.instrument("cron_manager_start.good_job", cron_jobs: @schedules) do
46
+ @running = true
47
+ schedules.each_key { |cron_key| create_task(cron_key) }
48
+ end
49
+ end
50
+
51
+ # Stop/cancel any scheduled tasks
52
+ # @param timeout [Numeric, nil] Unused but retained for compatibility
53
+ def shutdown(timeout: nil) # rubocop:disable Lint/UnusedMethodArgument
54
+ @running = false
55
+ @tasks.each do |_cron_key, task|
56
+ task.cancel
57
+ end
58
+ @tasks.clear
59
+ end
60
+
61
+ # Stop and restart
62
+ # @param timeout [Numeric, nil] Unused but retained for compatibility
63
+ def restart(timeout: nil) # rubocop:disable Lint/UnusedMethodArgument
64
+ shutdown
65
+ start
66
+ end
67
+
68
+ # Tests whether the manager is running.
69
+ # @return [Boolean, nil]
70
+ def running?
71
+ @running
72
+ end
73
+
74
+ # Tests whether the manager is shutdown.
75
+ # @return [Boolean, nil]
76
+ def shutdown?
77
+ !running?
78
+ end
79
+
80
+ # Enqueues a scheduled task
81
+ # @param cron_key [Symbol, String] the key within the schedule to use
82
+ def create_task(cron_key)
83
+ schedule = @schedules[cron_key]
84
+ return false if schedule.blank?
85
+
86
+ fugit = Fugit::Cron.parse(schedule.fetch(:cron))
87
+ delay = [(fugit.next_time - Time.current).to_f, 0].max
88
+
89
+ future = Concurrent::ScheduledTask.new(delay, args: [self, cron_key]) do |thr_scheduler, thr_cron_key|
90
+ # Re-schedule the next cron task before executing the current task
91
+ thr_scheduler.create_task(thr_cron_key)
92
+
93
+ CurrentExecution.reset
94
+ CurrentExecution.cron_key = thr_cron_key
95
+
96
+ Rails.application.executor.wrap do
97
+ schedule = thr_scheduler.schedules.fetch(thr_cron_key).with_indifferent_access
98
+ job_class = schedule.fetch(:class).constantize
99
+
100
+ job_set_value = schedule.fetch(:set, {})
101
+ job_set = job_set_value.respond_to?(:call) ? job_set_value.call : job_set_value
102
+
103
+ job_args_value = schedule.fetch(:args, [])
104
+ job_args = job_args_value.respond_to?(:call) ? job_args_value.call : job_args_value
105
+
106
+ job_class.set(job_set).perform_later(*job_args)
107
+ end
108
+ end
109
+
110
+ @tasks[cron_key] = future
111
+ future.add_observer(self.class, :task_observer)
112
+ future.execute
113
+ end
114
+ end
115
+ end
@@ -11,6 +11,12 @@ module GoodJob
11
11
  # @return [String, nil]
12
12
  thread_mattr_accessor :active_job_id
13
13
 
14
+ # @!attribute [rw] cron_key
15
+ # @!scope class
16
+ # Cron Key
17
+ # @return [String, nil]
18
+ thread_mattr_accessor :cron_key
19
+
14
20
  # @!attribute [rw] error_on_discard
15
21
  # @!scope class
16
22
  # Error captured by discard_on
data/lib/good_job/job.rb CHANGED
@@ -199,6 +199,7 @@ module GoodJob
199
199
  def self.enqueue(active_job, scheduled_at: nil, create_with_advisory_lock: false)
200
200
  ActiveSupport::Notifications.instrument("enqueue_job.good_job", { active_job: active_job, scheduled_at: scheduled_at, create_with_advisory_lock: create_with_advisory_lock }) do |instrument_payload|
201
201
  good_job_args = {
202
+ cron_key: CurrentExecution.cron_key,
202
203
  queue_name: active_job.queue_name.presence || DEFAULT_QUEUE_NAME,
203
204
  priority: active_job.priority || DEFAULT_PRIORITY,
204
205
  serialized_params: active_job.serialize,
@@ -285,6 +286,25 @@ module GoodJob
285
286
  super || serialized_params['job_id']
286
287
  end
287
288
 
289
+ def cron_key
290
+ if self.class.column_names.include?('cron_key')
291
+ super
292
+ else
293
+ ActiveSupport::Deprecation.warn(<<~DEPRECATION)
294
+ GoodJob has pending database migrations. To create the migration files, run:
295
+
296
+ rails generate good_job:update
297
+
298
+ To apply the migration files, run:
299
+
300
+ rails db:migrate
301
+
302
+ DEPRECATION
303
+
304
+ nil
305
+ end
306
+ end
307
+
288
308
  private
289
309
 
290
310
  # @return [ExecutionResult]
@@ -295,6 +315,7 @@ module GoodJob
295
315
 
296
316
  GoodJob::CurrentExecution.reset
297
317
  GoodJob::CurrentExecution.active_job_id = active_job_id
318
+ GoodJob::CurrentExecution.cron_key = cron_key
298
319
  ActiveSupport::Notifications.instrument("perform_job.good_job", { good_job: self, process_id: GoodJob::CurrentExecution.process_id, thread_name: GoodJob::CurrentExecution.thread_name }) do
299
320
  value = ActiveJob::Base.execute(params)
300
321
 
@@ -57,6 +57,16 @@ module GoodJob
57
57
  end
58
58
  end
59
59
 
60
+ # @!macro notification_responder
61
+ def cron_manager_start(event)
62
+ cron_jobs = event.payload[:cron_jobs]
63
+ cron_jobs_count = cron_jobs.size
64
+
65
+ info do
66
+ "GoodJob started cron with #{cron_jobs_count} #{'jobs'.pluralize(cron_jobs_count)}."
67
+ end
68
+ end
69
+
60
70
  # @!macro notification_responder
61
71
  def scheduler_shutdown_start(event)
62
72
  process_id = event.payload[:process_id]
@@ -3,6 +3,7 @@ module GoodJob
3
3
  # Ruby on Rails integration.
4
4
  class Railtie < ::Rails::Railtie
5
5
  config.good_job = ActiveSupport::OrderedOptions.new
6
+ config.good_job.cron = {}
6
7
 
7
8
  initializer "good_job.logger" do |_app|
8
9
  ActiveSupport.on_load(:good_job) do
@@ -23,6 +24,7 @@ module GoodJob
23
24
 
24
25
  config.after_initialize do
25
26
  GoodJob::Scheduler.instances.each(&:warm_cache)
27
+ GoodJob::CronManager.instances.each(&:start)
26
28
  end
27
29
  end
28
30
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
3
  # GoodJob gem version.
4
- VERSION = '1.11.3'
4
+ VERSION = '1.12.0'
5
5
  end
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: 1.11.3
4
+ version: 1.12.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: 2021-07-25 00:00:00.000000000 Z
11
+ date: 2021-07-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: 1.0.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: fugit
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '1.1'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '1.1'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: railties
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -345,12 +359,12 @@ files:
345
359
  - engine/app/helpers/good_job/application_helper.rb
346
360
  - engine/app/views/good_job/active_jobs/show.html.erb
347
361
  - engine/app/views/good_job/dashboards/index.html.erb
362
+ - engine/app/views/good_job/shared/_chart.erb
363
+ - engine/app/views/good_job/shared/_jobs_table.erb
364
+ - engine/app/views/good_job/shared/icons/_check.html.erb
365
+ - engine/app/views/good_job/shared/icons/_exclamation.html.erb
366
+ - engine/app/views/good_job/shared/icons/_trash.html.erb
348
367
  - engine/app/views/layouts/good_job/base.html.erb
349
- - engine/app/views/shared/_chart.erb
350
- - engine/app/views/shared/_jobs_table.erb
351
- - engine/app/views/shared/icons/_check.html.erb
352
- - engine/app/views/shared/icons/_exclamation.html.erb
353
- - engine/app/views/shared/icons/_trash.html.erb
354
368
  - engine/config/routes.rb
355
369
  - engine/lib/good_job/engine.rb
356
370
  - exe/good_job
@@ -367,6 +381,7 @@ files:
367
381
  - lib/good_job/adapter.rb
368
382
  - lib/good_job/cli.rb
369
383
  - lib/good_job/configuration.rb
384
+ - lib/good_job/cron_manager.rb
370
385
  - lib/good_job/current_execution.rb
371
386
  - lib/good_job/daemon.rb
372
387
  - lib/good_job/execution_result.rb