good_job 3.3.1 → 3.4.0

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: cabb124450eb5e88a460635fb4aa70f3e5a53406ded6554bb32018fa2aa890bc
4
- data.tar.gz: 0d83085806f8a1f30eff6136df4995f7982064b90a497ee56a4f8b9d922f4fbb
3
+ metadata.gz: 73efcb35d2a5e0ccfd13e2526a9a9d4a7195c4bf2e4a7f4127894a68c7f5dbb9
4
+ data.tar.gz: d9fddab346e5851e23daa5d9bef8c68b1fd5472f37d687c3cb49c5ff551cc31c
5
5
  SHA512:
6
- metadata.gz: 7f17d4379517d9909ac771c9920b3472b1d5bbb18ce45bd78b160f76eba6b6a1169e6b89ec044cd8f3c5ad93a311be45174bd43b99468c5ff9a5c4bad088263f
7
- data.tar.gz: 27f6aa3048d4035feb1e55b7cd4a3ba8410deb74ec9268fba23bac9a75c9db26bc3599d1f140bf68182b2f2307183013bc6601927a6eec19cab6051fcf03a927
6
+ metadata.gz: d720ab703536db9dfd396097aeb8b3aae64f5cd55a1754a9965eaaca275a2babfc4a05d249fb13896d776d02034553bd81cdda8b0af19a6f49d9a3a4bca29045
7
+ data.tar.gz: 9dd2af1f00637c3db171934777cc02be231e3ee98ad953aa9fcf030b00faa9fdd2b811f6e69094120a2deee8415e6a33d2d2a4a4d7701fbf89f7c45e272658ef
data/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.4.0](https://github.com/bensheldon/good_job/tree/v3.4.0) (2022-08-05)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.3.3...v3.4.0)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - Add cron\_enabled attribute to good\_job and pass it to process current state [\#675](https://github.com/bensheldon/good_job/pull/675) ([saksham-jain](https://github.com/saksham-jain))
10
+ - Reverse Dashboard Filter Hierarchy to be: queues+jobs then state [\#666](https://github.com/bensheldon/good_job/pull/666) ([bensheldon](https://github.com/bensheldon))
11
+ - Allow cron entries to be temporarily disabled and re-enabled through the Dashboard [\#649](https://github.com/bensheldon/good_job/pull/649) ([alex-klepa](https://github.com/alex-klepa))
12
+ - Add Configuration.total\_estimated\_threads to report number of threads consumed by GoodJob [\#645](https://github.com/bensheldon/good_job/pull/645) ([bensheldon](https://github.com/bensheldon))
13
+
14
+ **Closed issues:**
15
+
16
+ - Cron Schedule jobs add disable action [\#540](https://github.com/bensheldon/good_job/issues/540)
17
+
18
+ **Merged pull requests:**
19
+
20
+ - Removed text that implied an existing feature had not been finished [\#688](https://github.com/bensheldon/good_job/pull/688) ([pgvsalamander](https://github.com/pgvsalamander))
21
+
22
+ ## [v3.3.3](https://github.com/bensheldon/good_job/tree/v3.3.3) (2022-08-02)
23
+
24
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.3.2...v3.3.3)
25
+
26
+ **Fixed bugs:**
27
+
28
+ - Detect usage of `puma` CLI for async mode [\#686](https://github.com/bensheldon/good_job/pull/686) ([bensheldon](https://github.com/bensheldon))
29
+
30
+ **Closed issues:**
31
+
32
+ - Async not working Rails 7 with puma CLI [\#685](https://github.com/bensheldon/good_job/issues/685)
33
+
34
+ ## [v3.3.2](https://github.com/bensheldon/good_job/tree/v3.3.2) (2022-07-27)
35
+
36
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.3.1...v3.3.2)
37
+
38
+ **Fixed bugs:**
39
+
40
+ - Defer setting Adapter's execution mode until Rails initializes [\#683](https://github.com/bensheldon/good_job/pull/683) ([bensheldon](https://github.com/bensheldon))
41
+
3
42
  ## [v3.3.1](https://github.com/bensheldon/good_job/tree/v3.3.1) (2022-07-26)
4
43
 
5
44
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.3.0...v3.3.1)
data/README.md CHANGED
@@ -472,7 +472,7 @@ config.good_job.cron = {
472
472
  args: [42, "life"], # positional arguments to pass; can also be a proc e.g. `-> { [Time.now] }`
473
473
  kwargs: { name: "Alice" }, # keyword arguments to pass; can also be a proc e.g. `-> { { name: NAMES.sample } }`
474
474
  set: { priority: -10 }, # additional ActiveJob properties; can also be a lambda/proc e.g. `-> { { priority: [1,2].sample } }`
475
- description: "Something helpful", # optional description that appears in Dashboard (coming soon!)
475
+ description: "Something helpful", # optional description that appears in Dashboard
476
476
  },
477
477
  another_task: {
478
478
  cron: "0 0,12 * * *",
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
3
  class CronEntriesController < GoodJob::ApplicationController
4
+ before_action :check_settings_migration!, only: [:enable, :disable]
5
+
4
6
  def index
5
7
  @cron_entries = CronEntry.all
6
8
  end
@@ -15,5 +17,23 @@ module GoodJob
15
17
  @cron_entry.enqueue(Time.current)
16
18
  redirect_back(fallback_location: cron_entries_path, notice: "Cron entry has been enqueued.")
17
19
  end
20
+
21
+ def enable
22
+ @cron_entry = CronEntry.find(params[:cron_key])
23
+ @cron_entry.enable
24
+ redirect_back(fallback_location: cron_entries_path, notice: "Cron entry has been enabled.")
25
+ end
26
+
27
+ def disable
28
+ @cron_entry = CronEntry.find(params[:cron_key])
29
+ @cron_entry.disable
30
+ redirect_back(fallback_location: cron_entries_path, notice: "Cron entry has been disabled.")
31
+ end
32
+
33
+ private
34
+
35
+ def check_settings_migration!
36
+ redirect_back(fallback_location: cron_entries_path, alert: "Requires pending GoodJob database migration.") unless GoodJob::Setting.migrated?
37
+ end
18
38
  end
19
39
  end
@@ -24,18 +24,19 @@ module GoodJob
24
24
  @_last ||= records.last
25
25
  end
26
26
 
27
- def job_classes
28
- base_query.group("serialized_params->>'job_class'").count
29
- .sort_by { |name, _count| name.to_s }
30
- .to_h
31
- end
32
-
33
27
  def queues
34
28
  base_query.group(:queue_name).count
35
29
  .sort_by { |name, _count| name.to_s || EMPTY }
36
30
  .to_h
37
31
  end
38
32
 
33
+ def job_classes
34
+ filtered_query(params.slice(:queue_name)).unscope(:select)
35
+ .group("serialized_params->>'job_class'").count
36
+ .sort_by { |name, _count| name.to_s }
37
+ .to_h
38
+ end
39
+
39
40
  def states
40
41
  raise NotImplementedError
41
42
  end
@@ -51,7 +52,7 @@ module GoodJob
51
52
  }.merge(override).delete_if { |_, v| v.blank? }
52
53
  end
53
54
 
54
- def filtered_query
55
+ def filtered_query(filtered_params = params)
55
56
  raise NotImplementedError
56
57
  end
57
58
 
@@ -2,26 +2,27 @@
2
2
  module GoodJob
3
3
  class JobsFilter < BaseFilter
4
4
  def states
5
+ query = filtered_query(params.except(:state)).unscope(:select)
5
6
  {
6
- 'scheduled' => base_query.scheduled.count,
7
- 'retried' => base_query.retried.count,
8
- 'queued' => base_query.queued.count,
9
- 'running' => base_query.running.count,
10
- 'finished' => base_query.finished.count,
11
- 'discarded' => base_query.discarded.count,
7
+ 'scheduled' => query.scheduled.count,
8
+ 'retried' => query.retried.count,
9
+ 'queued' => query.queued.count,
10
+ 'running' => query.running.count,
11
+ 'finished' => query.finished.count,
12
+ 'discarded' => query.discarded.count,
12
13
  }
13
14
  end
14
15
 
15
- def filtered_query
16
+ def filtered_query(filter_params = params)
16
17
  query = base_query.includes(:executions).includes_advisory_locks
17
18
 
18
- query = query.job_class(params[:job_class]) if params[:job_class].present?
19
- query = query.where(queue_name: params[:queue_name]) if params[:queue_name].present?
20
- query = query.search_text(params[:query]) if params[:query].present?
21
- query = query.where(cron_key: params[:cron_key]) if params[:cron_key].present?
19
+ query = query.job_class(filter_params[:job_class]) if filter_params[:job_class].present?
20
+ query = query.where(queue_name: filter_params[:queue_name]) if filter_params[:queue_name].present?
21
+ query = query.search_text(filter_params[:query]) if filter_params[:query].present?
22
+ query = query.where(cron_key: filter_params[:cron_key]) if filter_params[:cron_key].present?
22
23
 
23
- if params[:state]
24
- case params[:state]
24
+ if filter_params[:state]
25
+ case filter_params[:state]
25
26
  when 'discarded'
26
27
  query = query.discarded
27
28
  when 'finished'
@@ -16,5 +16,15 @@ module GoodJob
16
16
  DEPRECATION
17
17
  nil
18
18
  end
19
+
20
+ # Checks for whether the schema is up to date.
21
+ # Can be overriden by child class.
22
+ # @return [Boolean]
23
+ def self.migrated?
24
+ return true if connection.table_exists?(table_name)
25
+
26
+ migration_pending_warning!
27
+ false
28
+ end
19
29
  end
20
30
  end
@@ -86,6 +86,20 @@ module GoodJob # :nodoc:
86
86
  end
87
87
  end
88
88
 
89
+ def enabled?
90
+ return true unless GoodJob::Setting.migrated?
91
+
92
+ GoodJob::Setting.cron_key_enabled?(key)
93
+ end
94
+
95
+ def enable
96
+ GoodJob::Setting.cron_key_enable(key)
97
+ end
98
+
99
+ def disable
100
+ GoodJob::Setting.cron_key_disable(key)
101
+ end
102
+
89
103
  def enqueue(cron_at = nil)
90
104
  GoodJob::CurrentThread.within do |current_thread|
91
105
  current_thread.cron_key = key
@@ -48,6 +48,7 @@ module GoodJob # :nodoc:
48
48
  preserve_job_records: GoodJob.preserve_job_records,
49
49
  retry_on_unhandled_error: GoodJob.retry_on_unhandled_error,
50
50
  schedulers: GoodJob::Scheduler.instances.map(&:name),
51
+ cron_enabled: GoodJob.configuration.enable_cron?,
51
52
  }
52
53
  end
53
54
 
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GoodJob
4
+ class Setting < BaseRecord
5
+ CRON_KEYS_DISABLED = "cron_keys_disabled"
6
+
7
+ self.table_name = 'good_job_settings'
8
+
9
+ def self.cron_key_enabled?(key)
10
+ cron_disabled = find_by(key: CRON_KEYS_DISABLED)&.value || []
11
+ cron_disabled.exclude?(key.to_s)
12
+ end
13
+
14
+ def self.cron_key_enable(key)
15
+ setting = GoodJob::Setting.find_by(key: CRON_KEYS_DISABLED)
16
+ return unless setting&.value&.include?(key.to_s)
17
+
18
+ setting.value.delete(key.to_s)
19
+ setting.save!
20
+ end
21
+
22
+ def self.cron_key_disable(key)
23
+ setting = find_or_initialize_by(key: CRON_KEYS_DISABLED) do |record|
24
+ record.value = []
25
+ end
26
+ setting.value << key
27
+ setting.save!
28
+ end
29
+ end
30
+ end
@@ -37,10 +37,20 @@
37
37
  <% end %>
38
38
  </div>
39
39
  <div class="col d-flex gap-3 justify-content-end">
40
- <%= button_to enqueue_cron_entry_path(cron_entry.id), method: :post, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: "Run cron entry now" }, title: "Run cron entry now", data: { confirm: "Confirm run cron entry now" } do %>
41
- <%= render "good_job/shared/icons/play" %>
42
- Run Now
40
+ <%= button_to enqueue_cron_entry_path(cron_entry), method: :post, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: "Enqueue cron entry now" }, title: "Enqueue cron entry now", data: { confirm: "Confirm enqueue cron entry now" } do %>
41
+ <%= render_icon "skip_forward" %>
43
42
  <% end %>
43
+
44
+ <% if cron_entry.enabled? %>
45
+ <%= button_to disable_cron_entry_path(cron_entry), method: :put, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: "Disable cron entry" }, title: "Disable cron entry", data: { confirm: "Confirm disable cron entry" } do %>
46
+ <%= render_icon "pause" %>
47
+ <% end %>
48
+ <% else %>
49
+ <%= button_to enable_cron_entry_path(cron_entry), method: :put, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: "Enable cron entry" }, title: "Enable cron entry", data: { confirm: "Confirm enable cron entry" } do %>
50
+ <%= render_icon "play" %>
51
+ <% end %>
52
+ <% end %>
53
+
44
54
  <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
45
55
  title: "Inspect",
46
56
  data: { bs_toggle: "collapse", bs_target: "##{dom_id(cron_entry, 'properties')}" },
@@ -2,14 +2,53 @@
2
2
  <div class="bg-light break-out">
3
3
  <h2 class="container-fluid pt-3 pb-2"><%= title %></h2>
4
4
 
5
- <ul class="nav nav-tabs bg-light px-3 mb-3">
5
+ <%= form_with(url: "", method: :get, local: true, id: "filter_form", class: "container-fluid") do |form| %>
6
+ <%= hidden_field_tag :poll, params[:poll] %>
7
+ <%= hidden_field_tag :state, params[:state] %>
8
+ <div class="d-flex flex-row w-100">
9
+ <div class="me-2">
10
+ <select name="queue_name" id="job_queue_filter" class="form-select form-select-sm">
11
+ <option value="" <%= "selected='selected'" if params[:queue_name].blank? %>>All queues</option>
12
+
13
+ <% filter.queues.each do |name, count| %>
14
+ <option value="<%= name.to_param %>" <%= "selected='selected'" if params[:queue_name] == name %>><%= name %> (<%= number_with_delimiter(count) %>)</option>
15
+ <% end %>
16
+ </select>
17
+ </div>
18
+
19
+ <div class="me-2">
20
+ <select name="job_class" id="job_class_filter" class="form-select form-select-sm">
21
+ <option value="" <%= "selected='selected'" if params[:job_class].blank? %>>All jobs</option>
22
+
23
+ <% filter.job_classes.each do |name, count| %>
24
+ <option value="<%= name.to_param %>" <%= "selected='selected'" if params[:job_class] == name %>><%= name %> (<%= number_with_delimiter(count) %>)</option>
25
+ <% end %>
26
+ </select>
27
+ </div>
28
+
29
+ <div class="me-2 flex-fill">
30
+ <%= search_field_tag "query", params[:query], class: "form-control form-control-sm", placeholder: "Search by class, job id, job params, and error text." %>
31
+ </div>
32
+
33
+ <div class="d-flex flex-col align-items-end">
34
+ <div>
35
+ <%= form.submit "Search", name: nil, class: "btn btn-primary btn-sm" %>
36
+ <%= link_to filter.to_params(job_class: nil, state: nil, queue_name: nil, query: nil), class: "btn btn-secondary btn-sm" do %>
37
+ Clear
38
+ <% end %>
39
+ </div>
40
+ </div>
41
+ </div>
42
+ <% end %>
43
+
44
+ <ul class="nav nav-tabs bg-light px-3 my-3">
6
45
  <li class="nav-item">
7
- <%= link_to "All", url_for(state: nil), class: "nav-link #{"active" unless params[:state]}" %>
46
+ <%= link_to "All", filter.to_params(state: nil), class: "nav-link #{"active" unless params[:state].present?}" %>
8
47
  </li>
9
48
 
10
49
  <% filter.states.each do |name, count| %>
11
50
  <li class="nav-item">
12
- <%= link_to url_for({state: name}), class: "nav-link #{"active" if params[:state] == name}" do %>
51
+ <%= link_to filter.to_params(state: name), class: "nav-link #{"active" if params[:state] == name}" do %>
13
52
  <%= t(name, scope: 'good_job.status') %>
14
53
  <span class="badge bg-primary rounded-pill <%= "bg-secondary" if count == 0 %>"><%= number_with_delimiter(count) %></span>
15
54
  <% end %>
@@ -18,43 +57,6 @@
18
57
  </ul>
19
58
  </div>
20
59
 
21
- <%= form_with(url: "", method: :get, local: true, id: "filter_form", class: "container-fluid") do |form| %>
22
- <%= hidden_field_tag :poll, params[:poll] %>
23
- <%= hidden_field_tag :state, params[:state] %>
24
- <div class="d-flex flex-row w-100">
25
- <div class="me-2">
26
- <select name="job_class" id="job_class_filter" class="form-select form-select-sm">
27
- <option value="" <%= "selected='selected'" if params[:job_class].blank? %>>All jobs</option>
28
-
29
- <% filter.job_classes.each do |name, count| %>
30
- <option value="<%= name.to_param %>" <%= "selected='selected'" if params[:job_class] == name %>><%= name %> (<%= count %>)</option>
31
- <% end %>
32
- </select>
33
- </div>
34
-
35
- <div class="me-2">
36
- <select name="queue_name" id="job_queue_filter" class="form-select form-select-sm">
37
- <option value="" <%= "selected='selected'" if params[:queue_name].blank? %>>All queues</option>
38
-
39
- <% filter.queues.each do |name, count| %>
40
- <option value="<%= name.to_param %>" <%= "selected='selected'" if params[:queue_name] == name %>><%= name %> (<%= count %>)</option>
41
- <% end %>
42
- </select>
43
- </div>
44
-
45
- <div class="me-2 flex-fill">
46
- <%= search_field_tag "query", params[:query], class: "form-control form-control-sm", placeholder: "Search by class, job id, job params, and error text." %>
47
- </div>
48
-
49
- <div class="d-flex flex-col align-items-end">
50
- <div>
51
- <%= form.submit "Search", name: nil, class: "btn btn-primary btn-sm" %>
52
- <%= link_to "Clear all", filter.to_params(job_class: nil, state: nil, queue_name: nil, query: nil), class: "btn btn-secondary btn-sm" %>
53
- </div>
54
- </div>
55
- </div>
56
- <% end %>
57
-
58
60
  <%= javascript_tag nonce: true do %>
59
61
  document.addEventListener("DOMContentLoaded", () => {
60
62
  document.querySelectorAll("#job_class_filter, #job_queue_filter").forEach((filter) => {
@@ -0,0 +1,4 @@
1
+ <!-- https://icons.getbootstrap.com/icons/pause-btn/ -->
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pause" viewBox="0 0 16 16">
3
+ <path d="M6 3.5a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-1 0V4a.5.5 0 0 1 .5-.5zm4 0a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-1 0V4a.5.5 0 0 1 .5-.5z" />
4
+ </svg>
data/config/routes.rb CHANGED
@@ -18,6 +18,8 @@ GoodJob::Engine.routes.draw do
18
18
  resources :cron_entries, only: %i[index show], param: :cron_key do
19
19
  member do
20
20
  post :enqueue
21
+ put :enable
22
+ put :disable
21
23
  end
22
24
  end
23
25
 
@@ -26,6 +26,13 @@ class CreateGoodJobs < ActiveRecord::Migration<%= migration_version %>
26
26
  t.jsonb :state
27
27
  end
28
28
 
29
+ create_table :good_job_settings, id: :uuid do |t|
30
+ t.timestamps
31
+ t.text :key
32
+ t.jsonb :value
33
+ t.index :key, unique: true
34
+ end
35
+
29
36
  add_index :good_jobs, :scheduled_at, where: "(finished_at IS NULL)", name: "index_good_jobs_on_scheduled_at"
30
37
  add_index :good_jobs, [:queue_name, :scheduled_at], where: "(finished_at IS NULL)", name: :index_good_jobs_on_queue_name_and_scheduled_at
31
38
  add_index :good_jobs, [:active_job_id, :created_at], name: :index_good_jobs_on_active_job_id_and_created_at
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ class CreateGoodJobSettings < ActiveRecord::Migration<%= migration_version %>
3
+ def change
4
+ reversible do |dir|
5
+ dir.up do
6
+ # Ensure this incremental update migration is idempotent
7
+ # with monolithic install migration.
8
+ return if connection.table_exists?(:good_job_settings)
9
+ end
10
+ end
11
+
12
+ create_table :good_job_settings, id: :uuid do |t|
13
+ t.timestamps
14
+ t.text :key
15
+ t.jsonb :value
16
+ t.index :key, unique: true
17
+ end
18
+ end
19
+ end
@@ -10,8 +10,6 @@ module GoodJob
10
10
  # @return [Array<GoodJob::Adapter>, nil]
11
11
  cattr_reader :instances, default: [], instance_reader: false
12
12
 
13
- attr_reader :execution_mode
14
-
15
13
  # @param execution_mode [Symbol, nil] specifies how and where jobs should be executed. You can also set this with the environment variable +GOOD_JOB_EXECUTION_MODE+.
16
14
  #
17
15
  # - +:inline+ executes jobs immediately in whatever process queued them (usually the web server process). This should only be used in test and development environments.
@@ -27,10 +25,10 @@ module GoodJob
27
25
  # - +production+ and all other environments: +:external+
28
26
  #
29
27
  def initialize(execution_mode: nil)
30
- @execution_mode = (execution_mode || GoodJob.configuration.execution_mode).to_sym
31
- GoodJob::Configuration.validate_execution_mode(@execution_mode)
32
- self.class.instances << self
28
+ @_execution_mode_override = execution_mode
29
+ GoodJob::Configuration.validate_execution_mode(@_execution_mode_override) if @_execution_mode_override
33
30
 
31
+ self.class.instances << self
34
32
  start_async if GoodJob.async_ready?
35
33
  end
36
34
 
@@ -94,6 +92,12 @@ module GoodJob
94
92
  @_async_started = false
95
93
  end
96
94
 
95
+ # This adapter's execution mode
96
+ # @return [Symbol, nil]
97
+ def execution_mode
98
+ @_execution_mode_override || GoodJob.configuration.execution_mode
99
+ end
100
+
97
101
  # Whether in +:async+ execution mode.
98
102
  # @return [Boolean]
99
103
  def execute_async?
@@ -104,7 +108,8 @@ module GoodJob
104
108
  # Whether in +:external+ execution mode.
105
109
  # @return [Boolean]
106
110
  def execute_externally?
107
- execution_mode == :external ||
111
+ execution_mode.nil? ||
112
+ execution_mode == :external ||
108
113
  (execution_mode.in?([:async, :async_server]) && !in_server_process?)
109
114
  end
110
115
 
@@ -143,10 +148,14 @@ module GoodJob
143
148
  def in_server_process?
144
149
  return @_in_server_process if defined? @_in_server_process
145
150
 
146
- @_in_server_process = Rails.const_defined?(:Server) ||
147
- caller.grep(%r{config.ru}).any? || # EXAMPLE: config.ru:3:in `block in <main>' OR config.ru:3:in `new_from_string'
148
- caller.grep(%{/rack/handler/}).any? || # EXAMPLE: iodine-0.7.44/lib/rack/handler/iodine.rb:13:in `start'
149
- (Concurrent.on_jruby? && caller.grep(%r{jruby/rack/rails_booter}).any?) # EXAMPLE: uri:classloader:/jruby/rack/rails_booter.rb:83:in `load_environment'
151
+ @_in_server_process = Rails.const_defined?(:Server) || begin
152
+ self_caller = caller
153
+
154
+ self_caller.grep(%r{config.ru}).any? || # EXAMPLE: config.ru:3:in `block in <main>' OR config.ru:3:in `new_from_string'
155
+ self_caller.grep(%r{puma/request}).any? || # EXAMPLE: puma-5.6.4/lib/puma/request.rb:76:in `handle_request'
156
+ self_caller.grep(%{/rack/handler/}).any? || # EXAMPLE: iodine-0.7.44/lib/rack/handler/iodine.rb:13:in `start'
157
+ (Concurrent.on_jruby? && self_caller.grep(%r{jruby/rack/rails_booter}).any?) # EXAMPLE: uri:classloader:/jruby/rack/rails_booter.rb:83:in `load_environment'
158
+ end
150
159
  end
151
160
  end
152
161
  end
@@ -42,6 +42,34 @@ module GoodJob
42
42
  # @return [Hash]
43
43
  attr_reader :env
44
44
 
45
+ # Returns the maximum number of threads GoodJob might consume
46
+ # @param warn [Boolean] whether to print a warning when over the limit
47
+ # @return [Integer]
48
+ def self.total_estimated_threads(warn: false)
49
+ configuration = new({})
50
+
51
+ cron_threads = configuration.enable_cron? ? 2 : 0
52
+ notifier_threads = 1
53
+ scheduler_threads = GoodJob::Scheduler.instances.sum { |scheduler| scheduler.stats[:max_threads] }
54
+
55
+ good_job_threads = cron_threads + notifier_threads + scheduler_threads
56
+ puma_threads = (Puma::Server.current&.max_threads if defined?(Puma::Server)) || 0
57
+
58
+ total_threads = good_job_threads + puma_threads
59
+ activerecord_pool_size = ActiveRecord::Base.connection_pool&.size
60
+
61
+ if warn && activerecord_pool_size && total_threads > activerecord_pool_size
62
+ message = "GoodJob is using #{good_job_threads} threads, " \
63
+ "#{" and Puma is using #{puma_threads} threads, " if puma_threads.positive?}" \
64
+ "which is #{total_threads - activerecord_pool_size} thread(s) more than ActiveRecord's database connection pool size of #{activerecord_pool_size}. " \
65
+ "Consider increasing ActiveRecord's database connection pool size in config/database.yml."
66
+
67
+ GoodJob.logger.warn message
68
+ end
69
+
70
+ good_job_threads
71
+ end
72
+
45
73
  # @param options [Hash] Any explicitly specified configuration options to
46
74
  # use. Keys are symbols that match the various methods on this class.
47
75
  # @param env [Hash] A +Hash+ from which to read environment variables that
@@ -89,7 +89,7 @@ module GoodJob # :nodoc:
89
89
  thr_scheduler.create_task(thr_cron_entry)
90
90
 
91
91
  Rails.application.executor.wrap do
92
- cron_entry.enqueue(thr_cron_at)
92
+ cron_entry.enqueue(thr_cron_at) if thr_cron_entry.enabled?
93
93
  end
94
94
  end
95
95
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
3
  # GoodJob gem version.
4
- VERSION = '3.3.1'
4
+ VERSION = '3.4.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: 3.3.1
4
+ version: 3.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: 2022-07-26 00:00:00.000000000 Z
11
+ date: 2022-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -379,6 +379,7 @@ files:
379
379
  - app/models/good_job/job.rb
380
380
  - app/models/good_job/lockable.rb
381
381
  - app/models/good_job/process.rb
382
+ - app/models/good_job/setting.rb
382
383
  - app/views/good_job/cron_entries/index.html.erb
383
384
  - app/views/good_job/cron_entries/show.html.erb
384
385
  - app/views/good_job/jobs/_executions.erb
@@ -398,6 +399,7 @@ files:
398
399
  - app/views/good_job/shared/icons/_dots.html.erb
399
400
  - app/views/good_job/shared/icons/_exclamation.html.erb
400
401
  - app/views/good_job/shared/icons/_info.html.erb
402
+ - app/views/good_job/shared/icons/_pause.html.erb
401
403
  - app/views/good_job/shared/icons/_play.html.erb
402
404
  - app/views/good_job/shared/icons/_skip_forward.html.erb
403
405
  - app/views/good_job/shared/icons/_stop.html.erb
@@ -413,6 +415,7 @@ files:
413
415
  - lib/generators/good_job/install_generator.rb
414
416
  - lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb
415
417
  - lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb.erb
418
+ - lib/generators/good_job/templates/update/migrations/02_create_good_job_settings.rb.erb
416
419
  - lib/generators/good_job/update_generator.rb
417
420
  - lib/good_job.rb
418
421
  - lib/good_job/active_job_extensions/concurrency.rb