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 +4 -4
- data/CHANGELOG.md +39 -0
- data/README.md +1 -1
- data/app/controllers/good_job/cron_entries_controller.rb +20 -0
- data/app/filters/good_job/base_filter.rb +8 -7
- data/app/filters/good_job/jobs_filter.rb +14 -13
- data/app/models/good_job/base_record.rb +10 -0
- data/app/models/good_job/cron_entry.rb +14 -0
- data/app/models/good_job/process.rb +1 -0
- data/app/models/good_job/setting.rb +30 -0
- data/app/views/good_job/cron_entries/index.html.erb +13 -3
- data/app/views/good_job/shared/_filter.erb +42 -40
- data/app/views/good_job/shared/icons/_pause.html.erb +4 -0
- data/config/routes.rb +2 -0
- data/lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb +7 -0
- data/lib/generators/good_job/templates/update/migrations/02_create_good_job_settings.rb.erb +19 -0
- data/lib/good_job/adapter.rb +19 -10
- data/lib/good_job/configuration.rb +28 -0
- data/lib/good_job/cron_manager.rb +1 -1
- data/lib/good_job/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 73efcb35d2a5e0ccfd13e2526a9a9d4a7195c4bf2e4a7f4127894a68c7f5dbb9
|
4
|
+
data.tar.gz: d9fddab346e5851e23daa5d9bef8c68b1fd5472f37d687c3cb49c5ff551cc31c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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' =>
|
7
|
-
'retried' =>
|
8
|
-
'queued' =>
|
9
|
-
'running' =>
|
10
|
-
'finished' =>
|
11
|
-
'discarded' =>
|
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(
|
19
|
-
query = query.where(queue_name:
|
20
|
-
query = query.search_text(
|
21
|
-
query = query.where(cron_key:
|
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
|
24
|
-
case
|
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
|
41
|
-
<%=
|
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
|
-
|
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",
|
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
|
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
@@ -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
|
data/lib/good_job/adapter.rb
CHANGED
@@ -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
|
-
@
|
31
|
-
GoodJob::Configuration.validate_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
|
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
|
-
|
148
|
-
|
149
|
-
|
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
|
data/lib/good_job/version.rb
CHANGED
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.
|
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-
|
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
|