good_job 3.12.5 → 3.12.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/app/controllers/good_job/frontends_controller.rb +45 -0
  4. data/app/{assets/good_job/modules → frontend/good_job}/application.js +4 -0
  5. data/app/frontend/good_job/vendor/chartjs/chart.min.js +19 -0
  6. data/app/frontend/good_job/vendor/rails_ujs.js +7 -0
  7. data/app/frontend/good_job/vendor/stimulus.js +2408 -0
  8. data/app/views/layouts/good_job/application.html.erb +8 -8
  9. data/config/routes.rb +3 -13
  10. data/lib/good_job/adapter.rb +9 -22
  11. data/lib/good_job/capsule.rb +83 -0
  12. data/lib/good_job/cli.rb +4 -10
  13. data/lib/good_job/version.rb +1 -1
  14. data/lib/good_job.rb +11 -14
  15. metadata +18 -16
  16. data/app/assets/good_job/vendor/chartjs/chart.min.js +0 -13
  17. data/app/assets/good_job/vendor/rails_ujs.js +0 -747
  18. data/app/controllers/good_job/assets_controller.rb +0 -47
  19. /data/app/{assets → frontend}/good_job/modules/charts.js +0 -0
  20. /data/app/{assets → frontend}/good_job/modules/checkbox_toggle.js +0 -0
  21. /data/app/{assets → frontend}/good_job/modules/document_ready.js +0 -0
  22. /data/app/{assets → frontend}/good_job/modules/live_poll.js +0 -0
  23. /data/app/{assets → frontend}/good_job/modules/popovers.js +0 -0
  24. /data/app/{assets → frontend}/good_job/modules/toasts.js +0 -0
  25. /data/app/{assets → frontend}/good_job/style.css +0 -0
  26. /data/app/{assets → frontend}/good_job/vendor/bootstrap/bootstrap.bundle.min.js +0 -0
  27. /data/app/{assets → frontend}/good_job/vendor/bootstrap/bootstrap.min.css +0 -0
  28. /data/app/{assets → frontend}/good_job/vendor/es_module_shims.js +0 -0
@@ -8,16 +8,16 @@
8
8
  <%= csp_meta_tag %>
9
9
 
10
10
  <%# Do not use asset tag helpers to avoid paths being overriden by config.asset_host %>
11
- <%= tag.link rel: "stylesheet", media: "screen", href: bootstrap_path(format: :css, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
12
- <%= tag.link rel: "stylesheet", media: "screen", href: style_path(format: :css, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
11
+ <%= tag.link rel: "stylesheet", href: frontend_static_path(:bootstrap, format: :css, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
12
+ <%= tag.link rel: "stylesheet", href: frontend_static_path(:style, format: :css, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
13
13
 
14
- <%= tag.script "", src: bootstrap_path(format: :js, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
15
- <%= tag.script "", src: chartjs_path(format: :js, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
16
- <%= tag.script "", src: rails_ujs_path(format: :js, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
14
+ <%= tag.script "", src: frontend_static_path(:bootstrap, format: :js, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
15
+ <%= tag.script "", src: frontend_static_path(:chartjs, format: :js, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
16
+ <%= tag.script "", src: frontend_static_path(:rails_ujs, format: :js, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
17
17
 
18
- <%= tag.script "", src: es_module_shims_path(format: :js, v: GoodJob::VERSION, locale: nil), async: true, nonce: content_security_policy_nonce %>
19
- <% importmaps = { imports: GoodJob::AssetsController.js_modules.keys.index_with { |module_name| modules_path(module_name, format: :js, locale: nil, v: GoodJob::VERSION) } } %>
20
- <%= tag.script importmaps.to_json.html_safe, type: "importmap", nonce: content_security_policy_nonce %>
18
+ <%= tag.script "", src: frontend_static_path(:es_module_shims, format: :js, v: GoodJob::VERSION, locale: nil), async: true, nonce: content_security_policy_nonce %>
19
+ <% importmaps = GoodJob::FrontendsController.js_modules.keys.index_with { |module_name| frontend_module_path(module_name, format: :js, locale: nil, v: GoodJob::VERSION) } %>
20
+ <%= tag.script({ imports: importmaps }.to_json.html_safe, type: "importmap", nonce: content_security_policy_nonce) %>
21
21
  <%= tag.script "", type: "module", nonce: content_security_policy_nonce do %> import "application"; <% end %>
22
22
  </head>
23
23
  <body>
data/config/routes.rb CHANGED
@@ -27,18 +27,8 @@ GoodJob::Engine.routes.draw do
27
27
 
28
28
  resources :processes, only: %i[index]
29
29
 
30
- scope :assets, controller: :assets do
31
- constraints(format: :css) do
32
- get :bootstrap, action: :bootstrap_css
33
- get :style, action: :style_css
34
- end
35
-
36
- constraints(format: :js) do
37
- get :bootstrap, action: :bootstrap_js
38
- get :chartjs, action: :chartjs_js
39
- get :rails_ujs, action: :rails_ujs_js
40
- get :es_module_shims, action: :es_module_shims_js
41
- get "modules/:module", action: :modules_js, as: :modules
42
- end
30
+ scope :frontend, controller: :frontends do
31
+ get "modules/:name", action: :module, as: :frontend_module, constraints: { format: 'js' }
32
+ get "static/:name", action: :static, as: :frontend_static, constraints: { format: %w[css js] }
43
33
  end
44
34
  end
@@ -24,9 +24,10 @@ module GoodJob
24
24
  # -+test+: +:inline+
25
25
  # - +production+ and all other environments: +:external+
26
26
  #
27
- def initialize(execution_mode: nil)
27
+ def initialize(execution_mode: nil, _capsule: GoodJob.capsule) # rubocop:disable Lint/UnderscorePrefixedVariableName
28
28
  @_execution_mode_override = execution_mode
29
29
  GoodJob::Configuration.validate_execution_mode(@_execution_mode_override) if @_execution_mode_override
30
+ @capsule = _capsule
30
31
 
31
32
  self.class.instances << self
32
33
  start_async if GoodJob.async_ready?
@@ -98,7 +99,7 @@ module GoodJob
98
99
  state = { queue_name: queue_name, count: executions_by_queue_and_scheduled_at.size }
99
100
  state[:scheduled_at] = scheduled_at if scheduled_at
100
101
 
101
- executed_locally = execute_async? && @scheduler&.create_thread(state)
102
+ executed_locally = execute_async? && @capsule&.create_thread(state)
102
103
  unless executed_locally
103
104
  state[:count] = job_id_to_active_jobs.values_at(*executions_by_queue_and_scheduled_at.map(&:active_job_id)).count { |active_job| send_notify?(active_job) }
104
105
  Notifier.notify(state) unless state[:count].zero?
@@ -141,7 +142,7 @@ module GoodJob
141
142
  job_state = { queue_name: execution.queue_name }
142
143
  job_state[:scheduled_at] = execution.scheduled_at if execution.scheduled_at
143
144
 
144
- executed_locally = execute_async? && @scheduler&.create_thread(job_state)
145
+ executed_locally = execute_async? && @capsule&.create_thread(job_state)
145
146
  Notifier.notify(job_state) if !executed_locally && send_notify?(active_job)
146
147
  end
147
148
 
@@ -150,20 +151,13 @@ module GoodJob
150
151
 
151
152
  # Shut down the thread pool executors.
152
153
  # @param timeout [nil, Numeric, Symbol] Seconds to wait for active threads.
153
- # * +nil+, the scheduler will trigger a shutdown but not wait for it to complete.
154
- # * +-1+, the scheduler will wait until the shutdown is complete.
155
- # * +0+, the scheduler will immediately shutdown and stop any threads.
154
+ # * +nil+ trigger a shutdown but not wait for it to complete.
155
+ # * +-1+ wait until the shutdown is complete.
156
+ # * +0+ immediately shutdown and stop any threads.
156
157
  # * A positive number will wait that many seconds before stopping any remaining active threads.
157
158
  # @return [void]
158
159
  def shutdown(timeout: :default)
159
- timeout = if timeout == :default
160
- GoodJob.configuration.shutdown_timeout
161
- else
162
- timeout
163
- end
164
-
165
- executables = [@notifier, @poller, @scheduler].compact
166
- GoodJob._shutdown_all(executables, timeout: timeout)
160
+ @capsule&.shutdown(timeout: timeout)
167
161
  @_async_started = false
168
162
  end
169
163
 
@@ -199,14 +193,7 @@ module GoodJob
199
193
  def start_async
200
194
  return unless execute_async?
201
195
 
202
- @notifier = GoodJob::Notifier.new(enable_listening: GoodJob.configuration.enable_listen_notify)
203
- @poller = GoodJob::Poller.new(poll_interval: GoodJob.configuration.poll_interval)
204
- @scheduler = GoodJob::Scheduler.from_configuration(GoodJob.configuration, warm_cache_on_initialize: true)
205
- @notifier.recipients << [@scheduler, :create_thread]
206
- @poller.recipients << [@scheduler, :create_thread]
207
-
208
- @cron_manager = GoodJob::CronManager.new(GoodJob.configuration.cron_entries, start_on_initialize: true) if GoodJob.configuration.enable_cron?
209
-
196
+ @capsule.start
210
197
  @_async_started = true
211
198
  end
212
199
 
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+ module GoodJob
3
+ # A GoodJob::Capsule contains the resources necessary to execute jobs, including
4
+ # a {GoodJob::Scheduler}, {GoodJob::Poller}, {GoodJob::Notifier}, and {GoodJob::CronManager}.
5
+ # GoodJob creates a default capsule on initialization.
6
+ class Capsule
7
+ # @!attribute [r] instances
8
+ # @!scope class
9
+ # List of all instantiated Capsules in the current process.
10
+ # @return [Array<GoodJob::Capsule>, nil]
11
+ cattr_reader :instances, default: [], instance_reader: false
12
+
13
+ # @param configuration [GoodJob::Configuration] Configuration to use for this capsule.
14
+ def initialize(configuration: GoodJob.configuration)
15
+ self.class.instances << self
16
+ @configuration = configuration
17
+
18
+ @autostart = true
19
+ @running = false
20
+ @mutex = Mutex.new
21
+ end
22
+
23
+ # Start executing jobs (if not already running).
24
+ def start
25
+ return if @running
26
+
27
+ @mutex.synchronize do
28
+ return if @running
29
+
30
+ @notifier = GoodJob::Notifier.new(enable_listening: @configuration.enable_listen_notify)
31
+ @poller = GoodJob::Poller.new(poll_interval: @configuration.poll_interval)
32
+ @scheduler = GoodJob::Scheduler.from_configuration(@configuration, warm_cache_on_initialize: true)
33
+ @notifier.recipients << [@scheduler, :create_thread]
34
+ @poller.recipients << [@scheduler, :create_thread]
35
+
36
+ @cron_manager = GoodJob::CronManager.new(@configuration.cron_entries, start_on_initialize: true) if @configuration.enable_cron?
37
+
38
+ @autostart = false
39
+ @running = true
40
+ end
41
+ end
42
+
43
+ # Shut down the thread pool executors.
44
+ # @param timeout [nil, Numeric, Symbol] Seconds to wait for active threads.
45
+ # * +-1+ will wait for all active threads to complete.
46
+ # * +0+ will interrupt active threads.
47
+ # * +N+ will wait at most N seconds and then interrupt active threads.
48
+ # * +nil+ will trigger a shutdown but not wait for it to complete.
49
+ # @return [void]
50
+ def shutdown(timeout: :default)
51
+ timeout = timeout == :default ? @configuration.shutdown_timeout : timeout
52
+ GoodJob._shutdown_all([@notifier, @poller, @scheduler, @cron_manager].compact, timeout: timeout)
53
+ @autostart = false
54
+ @running = false
55
+ end
56
+
57
+ # Shutdown and then start the capsule again.
58
+ # @param timeout [nil, Numeric, Symbol] Seconds to wait for active threads.
59
+ # @return [void]
60
+ def restart(timeout: :default)
61
+ shutdown(timeout: timeout)
62
+ start
63
+ end
64
+
65
+ # @return [Boolean] Whether the capsule is currently running.
66
+ def running?
67
+ @running
68
+ end
69
+
70
+ # @return [Boolean] Whether the capsule has been shutdown.
71
+ def shutdown?
72
+ [@notifier, @poller, @scheduler, @cron_manager].compact.all?(&:shutdown?)
73
+ end
74
+
75
+ # Creates an execution thread(s) with the given attributes.
76
+ # @param job_state [Hash, nil] See {GoodJob::Scheduler#create_thread}.
77
+ # @return [Boolean, nil] Whether work was started.
78
+ def create_thread(job_state = nil)
79
+ start if !running? && @autostart
80
+ @scheduler&.create_thread(job_state)
81
+ end
82
+ end
83
+ end
data/lib/good_job/cli.rb CHANGED
@@ -95,13 +95,8 @@ module GoodJob
95
95
 
96
96
  Daemon.new(pidfile: configuration.pidfile).daemonize if configuration.daemonize?
97
97
 
98
- notifier = GoodJob::Notifier.new(enable_listening: GoodJob.configuration.enable_listen_notify)
99
- poller = GoodJob::Poller.new(poll_interval: configuration.poll_interval)
100
- scheduler = GoodJob::Scheduler.from_configuration(configuration, warm_cache_on_initialize: true)
101
- notifier.recipients << [scheduler, :create_thread]
102
- poller.recipients << [scheduler, :create_thread]
103
-
104
- cron_manager = GoodJob::CronManager.new(configuration.cron_entries, start_on_initialize: true) if configuration.enable_cron?
98
+ capsule = GoodJob::Capsule.new
99
+ capsule.start
105
100
 
106
101
  if configuration.probe_port
107
102
  probe_server = GoodJob::ProbeServer.new(port: configuration.probe_port)
@@ -115,11 +110,10 @@ module GoodJob
115
110
 
116
111
  Kernel.loop do
117
112
  sleep 0.1
118
- break if @stop_good_job_executable || scheduler.shutdown? || notifier.shutdown?
113
+ break if @stop_good_job_executable || capsule.shutdown?
119
114
  end
120
115
 
121
- executors = [notifier, poller, cron_manager, scheduler].compact
122
- GoodJob._shutdown_all(executors, timeout: configuration.shutdown_timeout)
116
+ capsule.shutdown(timeout: configuration.shutdown_timeout)
123
117
  probe_server&.stop
124
118
  end
125
119
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
3
  # GoodJob gem version.
4
- VERSION = '3.12.5'
4
+ VERSION = '3.12.7'
5
5
  end
data/lib/good_job.rb CHANGED
@@ -15,6 +15,7 @@ require "good_job/active_job_extensions/notify_options"
15
15
 
16
16
  require "good_job/assignable_connection"
17
17
  require "good_job/bulk"
18
+ require "good_job/capsule"
18
19
  require "good_job/cleanup_tracker"
19
20
  require "good_job/cli"
20
21
  require "good_job/configuration"
@@ -91,6 +92,12 @@ module GoodJob
91
92
  # @return [GoodJob::Configuration, nil]
92
93
  mattr_accessor :configuration, default: GoodJob::Configuration.new({})
93
94
 
95
+ # @!attribute [rw] capsule
96
+ # @!scope class
97
+ # Global/default execution capsule for GoodJob.
98
+ # @return [GoodJob::Capsule, nil]
99
+ mattr_accessor :capsule, default: GoodJob::Capsule.new(configuration: configuration)
100
+
94
101
  # Called with exception when a GoodJob thread raises an exception
95
102
  # @param exception [Exception] Exception that was raised
96
103
  # @return [void]
@@ -108,16 +115,15 @@ module GoodJob
108
115
  # * +-1+, the scheduler will wait until the shutdown is complete.
109
116
  # * +0+, the scheduler will immediately shutdown and stop any active tasks.
110
117
  # * +1..+, the scheduler will wait that many seconds before stopping any remaining active tasks.
111
- # @param wait [Boolean] whether to wait for shutdown
112
118
  # @return [void]
113
119
  def self.shutdown(timeout: -1)
114
- _shutdown_all(_executables, timeout: timeout)
120
+ _shutdown_all(Capsule.instances, timeout: timeout)
115
121
  end
116
122
 
117
123
  # Tests whether jobs have stopped executing.
118
124
  # @return [Boolean] whether background threads are shut down
119
125
  def self.shutdown?
120
- _executables.all?(&:shutdown?)
126
+ Capsule.instances.all?(&:shutdown?)
121
127
  end
122
128
 
123
129
  # Stops and restarts executing jobs.
@@ -128,7 +134,7 @@ module GoodJob
128
134
  # @param timeout [Numeric, nil] Seconds to wait for active threads to finish.
129
135
  # @return [void]
130
136
  def self.restart(timeout: -1)
131
- _shutdown_all(_executables, :restart, timeout: timeout)
137
+ _shutdown_all(Capsule.instances, :restart, timeout: timeout)
132
138
  end
133
139
 
134
140
  # Sends +#shutdown+ or +#restart+ to executable objects ({GoodJob::Notifier}, {GoodJob::Poller}, {GoodJob::Scheduler}, {GoodJob::MultiScheduler}, {GoodJob::CronManager})
@@ -154,7 +160,7 @@ module GoodJob
154
160
  # analyze or inspect job performance.
155
161
  # If you are preserving job records this way, use this method regularly to
156
162
  # destroy old records and preserve space in your database.
157
- # @params older_than [nil,Numeric,ActiveSupport::Duration] Jobs older than this will be destroyed (default: +86400+).
163
+ # @param older_than [nil,Numeric,ActiveSupport::Duration] Jobs older than this will be destroyed (default: +86400+).
158
164
  # @return [Integer] Number of job execution records and batches that were destroyed.
159
165
  def self.cleanup_preserved_jobs(older_than: nil)
160
166
  older_than ||= GoodJob.configuration.cleanup_preserved_jobs_before_seconds_ago
@@ -194,14 +200,5 @@ module GoodJob
194
200
  end
195
201
  end
196
202
 
197
- def self._executables
198
- [].concat(
199
- CronManager.instances,
200
- Notifier.instances,
201
- Poller.instances,
202
- Scheduler.instances
203
- )
204
- end
205
-
206
203
  ActiveSupport.run_load_hooks(:good_job, self)
207
204
  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: 3.12.5
4
+ version: 3.12.7
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-02-24 00:00:00.000000000 Z
11
+ date: 2023-03-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -318,29 +318,30 @@ files:
318
318
  - CHANGELOG.md
319
319
  - LICENSE.txt
320
320
  - README.md
321
- - app/assets/good_job/modules/application.js
322
- - app/assets/good_job/modules/charts.js
323
- - app/assets/good_job/modules/checkbox_toggle.js
324
- - app/assets/good_job/modules/document_ready.js
325
- - app/assets/good_job/modules/live_poll.js
326
- - app/assets/good_job/modules/popovers.js
327
- - app/assets/good_job/modules/toasts.js
328
- - app/assets/good_job/style.css
329
- - app/assets/good_job/vendor/bootstrap/bootstrap.bundle.min.js
330
- - app/assets/good_job/vendor/bootstrap/bootstrap.min.css
331
- - app/assets/good_job/vendor/chartjs/chart.min.js
332
- - app/assets/good_job/vendor/es_module_shims.js
333
- - app/assets/good_job/vendor/rails_ujs.js
334
321
  - app/charts/good_job/scheduled_by_queue_chart.rb
335
322
  - app/controllers/good_job/application_controller.rb
336
- - app/controllers/good_job/assets_controller.rb
337
323
  - app/controllers/good_job/batches_controller.rb
338
324
  - app/controllers/good_job/cron_entries_controller.rb
325
+ - app/controllers/good_job/frontends_controller.rb
339
326
  - app/controllers/good_job/jobs_controller.rb
340
327
  - app/controllers/good_job/processes_controller.rb
341
328
  - app/filters/good_job/base_filter.rb
342
329
  - app/filters/good_job/batches_filter.rb
343
330
  - app/filters/good_job/jobs_filter.rb
331
+ - app/frontend/good_job/application.js
332
+ - app/frontend/good_job/modules/charts.js
333
+ - app/frontend/good_job/modules/checkbox_toggle.js
334
+ - app/frontend/good_job/modules/document_ready.js
335
+ - app/frontend/good_job/modules/live_poll.js
336
+ - app/frontend/good_job/modules/popovers.js
337
+ - app/frontend/good_job/modules/toasts.js
338
+ - app/frontend/good_job/style.css
339
+ - app/frontend/good_job/vendor/bootstrap/bootstrap.bundle.min.js
340
+ - app/frontend/good_job/vendor/bootstrap/bootstrap.min.css
341
+ - app/frontend/good_job/vendor/chartjs/chart.min.js
342
+ - app/frontend/good_job/vendor/es_module_shims.js
343
+ - app/frontend/good_job/vendor/rails_ujs.js
344
+ - app/frontend/good_job/vendor/stimulus.js
344
345
  - app/helpers/good_job/application_helper.rb
345
346
  - app/models/concerns/good_job/filterable.rb
346
347
  - app/models/concerns/good_job/reportable.rb
@@ -408,6 +409,7 @@ files:
408
409
  - lib/good_job/adapter.rb
409
410
  - lib/good_job/assignable_connection.rb
410
411
  - lib/good_job/bulk.rb
412
+ - lib/good_job/capsule.rb
411
413
  - lib/good_job/cleanup_tracker.rb
412
414
  - lib/good_job/cli.rb
413
415
  - lib/good_job/configuration.rb