good_job 3.26.2 → 3.27.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 +26 -0
- data/README.md +14 -3
- data/app/controllers/good_job/metrics_controller.rb +10 -0
- data/app/filters/good_job/base_filter.rb +4 -0
- data/app/filters/good_job/jobs_filter.rb +4 -0
- data/app/frontend/good_job/style.css +4 -0
- data/app/models/concerns/good_job/advisory_lockable.rb +2 -7
- data/app/models/good_job/batch_record.rb +1 -0
- data/app/models/good_job/cron_entry.rb +6 -1
- data/app/models/good_job/discrete_execution.rb +1 -0
- data/app/models/good_job/execution.rb +2 -1
- data/app/models/good_job/job.rb +1 -0
- data/app/models/good_job/process.rb +1 -0
- data/app/models/good_job/setting.rb +29 -10
- data/app/views/good_job/shared/_filter.erb +4 -4
- data/app/views/good_job/shared/_navbar.erb +1 -1
- data/config/locales/de.yml +1 -1
- data/config/locales/en.yml +1 -1
- data/config/locales/es.yml +1 -1
- data/config/locales/fr.yml +3 -3
- data/config/locales/it.yml +1 -1
- data/config/locales/nl.yml +1 -1
- data/config/locales/ru.yml +1 -1
- data/config/routes.rb +1 -0
- data/lib/good_job/active_job_extensions/concurrency.rb +66 -17
- data/lib/good_job/version.rb +1 -1
- metadata +17 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2b3817133850acc5176086af47b774bcb80a6ead4ee11e1121a10b23b5c7adb0
|
|
4
|
+
data.tar.gz: 0d8d3412835f37b23122a821d7f1ea1f20120a1e1893d29fadcd6e6b8697bce5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 984697d136c831db59efdd76f8ca3fc820263de06bdcfe7650f685d75ee0ef64cc3e4c39aa2919b84273b15bb15689bc1fb47afce99a597035266c70372a7c65
|
|
7
|
+
data.tar.gz: f6f5942330d621fd89cc951a0f6731aa87578725f5959c9dfcf13c99252952e9c610385afcb03cc17a8bdeeb6a126d607e18c910b66353c01c941eb8e0a6c515
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v3.27.0](https://github.com/bensheldon/good_job/tree/v3.27.0) (2024-03-24)
|
|
4
|
+
|
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.26.2...v3.27.0)
|
|
6
|
+
|
|
7
|
+
**Implemented enhancements:**
|
|
8
|
+
|
|
9
|
+
- Add `enabled_by_default: false` as option for cron configuration [\#1289](https://github.com/bensheldon/good_job/pull/1289) ([bensheldon](https://github.com/bensheldon))
|
|
10
|
+
- Load metrics for job statuses asynchronously [\#1286](https://github.com/bensheldon/good_job/pull/1286) ([binarygit](https://github.com/binarygit))
|
|
11
|
+
- Implement throttling options in concurrency extension [\#1270](https://github.com/bensheldon/good_job/pull/1270) ([marckohlbrugge](https://github.com/marckohlbrugge))
|
|
12
|
+
|
|
13
|
+
**Fixed bugs:**
|
|
14
|
+
|
|
15
|
+
- fix\(ui-dropdown\): use dropdown-end on locales dropdown [\#1296](https://github.com/bensheldon/good_job/pull/1296) ([WailanTirajoh](https://github.com/WailanTirajoh))
|
|
16
|
+
|
|
17
|
+
**Closed issues:**
|
|
18
|
+
|
|
19
|
+
- Disabling probe [\#1290](https://github.com/bensheldon/good_job/issues/1290)
|
|
20
|
+
- Set an implicit order on models [\#1242](https://github.com/bensheldon/good_job/issues/1242)
|
|
21
|
+
|
|
22
|
+
**Merged pull requests:**
|
|
23
|
+
|
|
24
|
+
- docs\(readme\): remove double "using" [\#1295](https://github.com/bensheldon/good_job/pull/1295) ([WailanTirajoh](https://github.com/WailanTirajoh))
|
|
25
|
+
- Set an implicit order on models [\#1293](https://github.com/bensheldon/good_job/pull/1293) ([mec](https://github.com/mec))
|
|
26
|
+
- CI: install gems after loading cache, not before [\#1288](https://github.com/bensheldon/good_job/pull/1288) ([bensheldon](https://github.com/bensheldon))
|
|
27
|
+
- Ensure job execution Advisory Lock query uses bind parameters [\#1287](https://github.com/bensheldon/good_job/pull/1287) ([bensheldon](https://github.com/bensheldon))
|
|
28
|
+
|
|
3
29
|
## [v3.26.2](https://github.com/bensheldon/good_job/tree/v3.26.2) (2024-03-15)
|
|
4
30
|
|
|
5
31
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.26.1...v3.26.2)
|
data/README.md
CHANGED
|
@@ -127,7 +127,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
|
|
|
127
127
|
- Because of Rails deferred autoloading, jobs enqueued via the `rails console` may not begin executing on a separate server process until the Rails application is fully initialized by loading a web page once.
|
|
128
128
|
- Remember, only Active Job's `perform_later` sends jobs to the queue adapter; Active Job's `perform_now` executes the job immediately and does not invoke the queue adapter. GoodJob is not involved in `perform_now` jobs.
|
|
129
129
|
1. **In Rails' test environment**, by default, GoodJob's Adapter executes jobs `inline` immediately in the current thread.
|
|
130
|
-
- Future-scheduled jobs can be executed with `GoodJob.perform_inline` using
|
|
130
|
+
- Future-scheduled jobs can be executed with `GoodJob.perform_inline` using a tool like Timecop or `ActiveSupport::Testing::TimeHelpers`.
|
|
131
131
|
- Note that Active Job's TestAdapter, which powers test helpers (e.g. `assert_enqueued_with`), may override GoodJob's Adapter in [some configurations](https://github.com/rails/rails/issues/37270).
|
|
132
132
|
1. **In Rails' production environment**, by default, GoodJob's Adapter enqueues jobs in `external` mode to be executed by a separate execution process:
|
|
133
133
|
- By default, GoodJob separates job enqueuing from job execution so that jobs can be scaled independently of the web server. Use the GoodJob command-line tool to execute jobs:
|
|
@@ -541,6 +541,16 @@ class MyJob < ApplicationJob
|
|
|
541
541
|
# Can be an Integer or Lambda/Proc that is invoked in the context of the job
|
|
542
542
|
perform_limit: 1,
|
|
543
543
|
|
|
544
|
+
# Maximum number of jobs with the concurrency key to be enqueued within
|
|
545
|
+
# the time period, looking backwards from the current time. Must be an array
|
|
546
|
+
# with two elements: the number of jobs and the time period.
|
|
547
|
+
enqueue_throttle: [10, 1.minute],
|
|
548
|
+
|
|
549
|
+
# Maximum number of jobs with the concurrency key to be performed within
|
|
550
|
+
# the time period, looking backwards from the current time. Must be an array
|
|
551
|
+
# with two elements: the number of jobs and the time period.
|
|
552
|
+
perform_throttle: [100, 1.hour],
|
|
553
|
+
|
|
544
554
|
# Note: Under heavy load, the total number of jobs may exceed the
|
|
545
555
|
# sum of `enqueue_limit` and `perform_limit` because of race conditions
|
|
546
556
|
# caused by imperfectly disjunctive states. If you need to constrain
|
|
@@ -617,9 +627,10 @@ config.good_job.cron = {
|
|
|
617
627
|
set: { priority: -10 }, # additional Active Job properties; can also be a lambda/proc e.g. `-> { { priority: [1,2].sample } }`
|
|
618
628
|
description: "Something helpful", # optional description that appears in Dashboard
|
|
619
629
|
},
|
|
620
|
-
|
|
630
|
+
production_task: {
|
|
621
631
|
cron: "0 0,12 * * *",
|
|
622
|
-
class: "
|
|
632
|
+
class: "ProductionJob",
|
|
633
|
+
enabled_by_default: -> { Rails.env.production? } # Only enable in production, otherwise can be enabled manually through Dashboard
|
|
623
634
|
},
|
|
624
635
|
complex_schedule: {
|
|
625
636
|
class: "ComplexScheduleJob",
|
|
@@ -16,10 +16,20 @@ module GoodJob
|
|
|
16
16
|
}
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
def job_status
|
|
20
|
+
@filter = JobsFilter.new(params)
|
|
21
|
+
|
|
22
|
+
render json: @filter.states.transform_values { |count| number_with_delimiter(count) }
|
|
23
|
+
end
|
|
24
|
+
|
|
19
25
|
private
|
|
20
26
|
|
|
21
27
|
def number_to_human(count)
|
|
22
28
|
helpers.number_to_human(count, **helpers.translate_hash("good_job.number.human.decimal_units"))
|
|
23
29
|
end
|
|
30
|
+
|
|
31
|
+
def number_with_delimiter(count)
|
|
32
|
+
helpers.number_with_delimiter(count, **helpers.translate_hash('good_job.number.format'))
|
|
33
|
+
end
|
|
24
34
|
end
|
|
25
35
|
end
|
|
@@ -44,13 +44,8 @@ module GoodJob
|
|
|
44
44
|
cte_table = Arel::Table.new(:rows)
|
|
45
45
|
cte_query = original_query.select(primary_key, column).except(:limit)
|
|
46
46
|
cte_query = cte_query.limit(select_limit) if select_limit
|
|
47
|
-
cte_type =
|
|
48
|
-
|
|
49
|
-
else
|
|
50
|
-
''
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
composed_cte = Arel::Nodes::As.new(cte_table, Arel::Nodes::SqlLiteral.new([cte_type, "(", cte_query.to_sql, ")"].join(' ')))
|
|
47
|
+
cte_type = supports_cte_materialization_specifiers? ? :MATERIALIZED : :""
|
|
48
|
+
composed_cte = Arel::Nodes::As.new(cte_table, Arel::Nodes::UnaryOperation.new(cte_type, cte_query.arel))
|
|
54
49
|
query = cte_table.project(cte_table[:id])
|
|
55
50
|
.with(composed_cte)
|
|
56
51
|
.where(Arel.sql("#{function}(('x' || substr(md5(#{connection.quote(table_name)} || '-' || #{connection.quote_table_name(cte_table.name)}.#{connection.quote_column_name(column)}::text), 1, 16))::bit(64)::bigint)"))
|
|
@@ -7,6 +7,7 @@ module GoodJob
|
|
|
7
7
|
include AdvisoryLockable
|
|
8
8
|
|
|
9
9
|
self.table_name = 'good_job_batches'
|
|
10
|
+
self.implicit_order_column = 'created_at'
|
|
10
11
|
|
|
11
12
|
has_many :jobs, class_name: 'GoodJob::Job', inverse_of: :batch, foreign_key: :batch_id, dependent: nil
|
|
12
13
|
has_many :executions, class_name: 'GoodJob::Execution', foreign_key: :batch_id, inverse_of: :batch, dependent: nil
|
|
@@ -74,7 +74,7 @@ module GoodJob # :nodoc:
|
|
|
74
74
|
def enabled?
|
|
75
75
|
return true unless GoodJob::Setting.migrated?
|
|
76
76
|
|
|
77
|
-
GoodJob::Setting.cron_key_enabled?(key)
|
|
77
|
+
GoodJob::Setting.cron_key_enabled?(key, default: enabled_by_default?)
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
def enable
|
|
@@ -132,6 +132,11 @@ module GoodJob # :nodoc:
|
|
|
132
132
|
|
|
133
133
|
private
|
|
134
134
|
|
|
135
|
+
def enabled_by_default?
|
|
136
|
+
value = params.fetch(:enabled_by_default, true)
|
|
137
|
+
value.respond_to?(:call) ? value.call : value
|
|
138
|
+
end
|
|
139
|
+
|
|
135
140
|
def cron
|
|
136
141
|
params.fetch(:cron)
|
|
137
142
|
end
|
|
@@ -5,6 +5,7 @@ module GoodJob # :nodoc:
|
|
|
5
5
|
include ErrorEvents
|
|
6
6
|
|
|
7
7
|
self.table_name = 'good_job_executions'
|
|
8
|
+
self.implicit_order_column = 'created_at'
|
|
8
9
|
|
|
9
10
|
belongs_to :execution, class_name: 'GoodJob::Execution', foreign_key: 'active_job_id', primary_key: 'active_job_id', inverse_of: :discrete_executions, optional: true
|
|
10
11
|
belongs_to :job, class_name: 'GoodJob::Job', foreign_key: 'active_job_id', primary_key: 'active_job_id', inverse_of: :discrete_executions, optional: true
|
|
@@ -16,6 +16,7 @@ module GoodJob
|
|
|
16
16
|
|
|
17
17
|
self.table_name = 'good_jobs'
|
|
18
18
|
self.advisory_lockable_column = 'active_job_id'
|
|
19
|
+
self.implicit_order_column = 'created_at'
|
|
19
20
|
|
|
20
21
|
define_model_callbacks :perform
|
|
21
22
|
define_model_callbacks :perform_unlocked, only: :after
|
|
@@ -94,7 +95,7 @@ module GoodJob
|
|
|
94
95
|
# @!method only_scheduled
|
|
95
96
|
# @!scope class
|
|
96
97
|
# @return [ActiveRecord::Relation]
|
|
97
|
-
scope :only_scheduled, -> { where(arel_table['scheduled_at'].lteq(Time.current)).or(where(scheduled_at: nil)) }
|
|
98
|
+
scope :only_scheduled, -> { where(arel_table['scheduled_at'].lteq(Arel::Nodes::BindParam.new(ActiveModel::Attribute.with_cast_value("scheduled_at", Time.current, ActiveModel::Type::DateTime.new)))).or(where(scheduled_at: nil)) }
|
|
98
99
|
|
|
99
100
|
# Order executions by priority (highest priority first).
|
|
100
101
|
# @!method priority_ordered
|
data/app/models/good_job/job.rb
CHANGED
|
@@ -26,6 +26,7 @@ module GoodJob
|
|
|
26
26
|
|
|
27
27
|
self.primary_key = 'active_job_id'
|
|
28
28
|
self.advisory_lockable_column = 'active_job_id'
|
|
29
|
+
self.implicit_order_column = 'created_at'
|
|
29
30
|
|
|
30
31
|
belongs_to :batch, class_name: 'GoodJob::BatchRecord', inverse_of: :jobs, optional: true
|
|
31
32
|
has_many :executions, -> { order(created_at: :asc) }, class_name: 'GoodJob::Execution', foreign_key: 'active_job_id', inverse_of: :job # rubocop:disable Rails/HasManyOrHasOneDependent
|
|
@@ -2,29 +2,48 @@
|
|
|
2
2
|
|
|
3
3
|
module GoodJob
|
|
4
4
|
class Setting < BaseRecord
|
|
5
|
+
CRON_KEYS_ENABLED = "cron_keys_enabled"
|
|
5
6
|
CRON_KEYS_DISABLED = "cron_keys_disabled"
|
|
6
7
|
|
|
7
8
|
self.table_name = 'good_job_settings'
|
|
9
|
+
self.implicit_order_column = 'created_at'
|
|
8
10
|
|
|
9
|
-
def self.cron_key_enabled?(key)
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
def self.cron_key_enabled?(key, default: true)
|
|
12
|
+
if default
|
|
13
|
+
cron_disabled = find_by(key: CRON_KEYS_DISABLED)&.value || []
|
|
14
|
+
cron_disabled.exclude?(key.to_s)
|
|
15
|
+
else
|
|
16
|
+
cron_enabled = find_by(key: CRON_KEYS_ENABLED)&.value || []
|
|
17
|
+
cron_enabled.include?(key.to_s)
|
|
18
|
+
end
|
|
12
19
|
end
|
|
13
20
|
|
|
14
21
|
def self.cron_key_enable(key)
|
|
15
|
-
|
|
16
|
-
|
|
22
|
+
enabled_setting = find_or_initialize_by(key: CRON_KEYS_ENABLED) do |record|
|
|
23
|
+
record.value = []
|
|
24
|
+
end
|
|
25
|
+
enabled_setting.value << key unless enabled_setting.value.include?(key)
|
|
26
|
+
enabled_setting.save!
|
|
17
27
|
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
disabled_setting = GoodJob::Setting.find_by(key: CRON_KEYS_DISABLED)
|
|
29
|
+
return unless disabled_setting&.value&.include?(key.to_s)
|
|
30
|
+
|
|
31
|
+
disabled_setting.value.delete(key.to_s)
|
|
32
|
+
disabled_setting.save!
|
|
20
33
|
end
|
|
21
34
|
|
|
22
35
|
def self.cron_key_disable(key)
|
|
23
|
-
|
|
36
|
+
enabled_setting = GoodJob::Setting.find_by(key: CRON_KEYS_ENABLED)
|
|
37
|
+
if enabled_setting&.value&.include?(key.to_s)
|
|
38
|
+
enabled_setting.value.delete(key.to_s)
|
|
39
|
+
enabled_setting.save!
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
disabled_setting = find_or_initialize_by(key: CRON_KEYS_DISABLED) do |record|
|
|
24
43
|
record.value = []
|
|
25
44
|
end
|
|
26
|
-
|
|
27
|
-
|
|
45
|
+
disabled_setting.value << key unless disabled_setting.value.include?(key)
|
|
46
|
+
disabled_setting.save!
|
|
28
47
|
end
|
|
29
48
|
end
|
|
30
49
|
end
|
|
@@ -47,16 +47,16 @@
|
|
|
47
47
|
</div>
|
|
48
48
|
<% end %>
|
|
49
49
|
|
|
50
|
-
<ul data-live-poll-region="filter-tabs" class="nav nav-tabs my-3">
|
|
50
|
+
<ul data-controller="async-values" data-async-values-url-value="<%= metrics_job_status_path %>" data-live-poll-region="filter-tabs" class="nav nav-tabs my-3">
|
|
51
51
|
<li class="nav-item">
|
|
52
52
|
<%= link_to t(".all"), filter.to_params(state: nil), class: "nav-link #{"active" unless params[:state].present?}" %>
|
|
53
53
|
</li>
|
|
54
54
|
|
|
55
|
-
<% filter.
|
|
55
|
+
<% filter.state_names.each do |name| %>
|
|
56
56
|
<li class="nav-item">
|
|
57
57
|
<%= link_to filter.to_params(state: name), class: "nav-link #{"active" if params[:state] == name}" do %>
|
|
58
|
-
<%= t(name, scope: 'good_job.status'
|
|
59
|
-
<span
|
|
58
|
+
<%= t(name, scope: 'good_job.status') %>
|
|
59
|
+
<span data-async-values-target="value", data-async-values-key="<%= name %>" data-async-values-zero-class="bg-secondary" class="badge bg-primary rounded-pill d-none"></span>
|
|
60
60
|
<% end %>
|
|
61
61
|
</li>
|
|
62
62
|
<% end %>
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
<%= I18n.locale %>
|
|
74
74
|
</a>
|
|
75
75
|
|
|
76
|
-
<ul class="dropdown-menu" aria-labelledby="localeOptions">
|
|
76
|
+
<ul class="dropdown-menu dropdown-menu-end min-w-auto" aria-labelledby="localeOptions">
|
|
77
77
|
<% possible_locales = I18n.available_locales %>
|
|
78
78
|
<% possible_locales.reject { |locale| locale == I18n.locale }.each do |locale| %>
|
|
79
79
|
<li><%= link_to locale, url_for(locale: locale), class: "dropdown-item" %></li>
|
data/config/locales/de.yml
CHANGED
|
@@ -34,7 +34,7 @@ de:
|
|
|
34
34
|
cron_entries:
|
|
35
35
|
actions:
|
|
36
36
|
confirm_disable: Möchten Sie diesen Cron-Eintrag wirklich deaktivieren?
|
|
37
|
-
confirm_enable: Möchten Sie diesen Cron-Eintrag wirklich
|
|
37
|
+
confirm_enable: Möchten Sie diesen Cron-Eintrag wirklich aktivieren?
|
|
38
38
|
confirm_enqueue: Möchten Sie diesen Cron-Eintrag wirklich in die Warteschlange stellen?
|
|
39
39
|
disable: Cron-Eintrag deaktivieren
|
|
40
40
|
enable: Cron-Eintrag aktivieren
|
data/config/locales/en.yml
CHANGED
|
@@ -34,7 +34,7 @@ en:
|
|
|
34
34
|
cron_entries:
|
|
35
35
|
actions:
|
|
36
36
|
confirm_disable: Are you sure you want to disable this cron entry?
|
|
37
|
-
confirm_enable: Are you sure you want to
|
|
37
|
+
confirm_enable: Are you sure you want to enable this cron entry?
|
|
38
38
|
confirm_enqueue: Are you sure you want to enqueue this cron entry?
|
|
39
39
|
disable: Disable cron entry
|
|
40
40
|
enable: Enable cron entry
|
data/config/locales/es.yml
CHANGED
|
@@ -34,7 +34,7 @@ es:
|
|
|
34
34
|
cron_entries:
|
|
35
35
|
actions:
|
|
36
36
|
confirm_disable: "¿Estás seguro que querés deshabilitar esta tarea programada?"
|
|
37
|
-
confirm_enable: "¿Estás seguro que querés
|
|
37
|
+
confirm_enable: "¿Estás seguro que querés habilitar esta tarea programada?"
|
|
38
38
|
confirm_enqueue: "¿Estás seguro que querés encolar esta tarea programada?"
|
|
39
39
|
disable: Deshabilitar tarea programada
|
|
40
40
|
enable: Habilitar tarea programada
|
data/config/locales/fr.yml
CHANGED
|
@@ -33,9 +33,9 @@ fr:
|
|
|
33
33
|
no_batches_found: Aucun lot trouvé.
|
|
34
34
|
cron_entries:
|
|
35
35
|
actions:
|
|
36
|
-
confirm_disable: Voulez-vous vraiment désactiver cette entrée cron
|
|
37
|
-
confirm_enable: Voulez-vous vraiment activer cette entrée cron
|
|
38
|
-
confirm_enqueue: Voulez-vous vraiment mettre en file d'attente cette entrée cron
|
|
36
|
+
confirm_disable: Voulez-vous vraiment désactiver cette entrée cron?
|
|
37
|
+
confirm_enable: Voulez-vous vraiment activer cette entrée cron?
|
|
38
|
+
confirm_enqueue: Voulez-vous vraiment mettre en file d'attente cette entrée cron?
|
|
39
39
|
disable: Désactiver l'entrée cron
|
|
40
40
|
enable: Activer l'entrée cron
|
|
41
41
|
enqueue: Mettre en file d'attente l'entrée cron maintenant
|
data/config/locales/it.yml
CHANGED
|
@@ -34,7 +34,7 @@ it:
|
|
|
34
34
|
cron_entries:
|
|
35
35
|
actions:
|
|
36
36
|
confirm_disable: Sei sicuro di voler disabilitare questa voce cron?
|
|
37
|
-
confirm_enable: Sei sicuro di voler
|
|
37
|
+
confirm_enable: Sei sicuro di voler abilita questa voce cron?
|
|
38
38
|
confirm_enqueue: Sei sicuro di voler mettere in coda questa voce cron?
|
|
39
39
|
disable: Disabilita voce cron
|
|
40
40
|
enable: Abilita voce cron
|
data/config/locales/nl.yml
CHANGED
|
@@ -34,7 +34,7 @@ nl:
|
|
|
34
34
|
cron_entries:
|
|
35
35
|
actions:
|
|
36
36
|
confirm_disable: Weet u zeker dat u deze cron-vermelding wilt uitschakelen?
|
|
37
|
-
confirm_enable: Weet u zeker dat u deze cron-invoer wilt
|
|
37
|
+
confirm_enable: Weet u zeker dat u deze cron-invoer wilt inschakelen?
|
|
38
38
|
confirm_enqueue: Weet u zeker dat u deze cron-vermelding in de wachtrij wilt plaatsen?
|
|
39
39
|
disable: Schakel cron-invoer uit
|
|
40
40
|
enable: Schakel cron-invoer in
|
data/config/locales/ru.yml
CHANGED
|
@@ -34,7 +34,7 @@ ru:
|
|
|
34
34
|
cron_entries:
|
|
35
35
|
actions:
|
|
36
36
|
confirm_disable: Вы уверены, что хотите отключить эту задачу cron?
|
|
37
|
-
confirm_enable: Вы уверены, что хотите
|
|
37
|
+
confirm_enable: Вы уверены, что хотите включить это задание cron?
|
|
38
38
|
confirm_enqueue: Вы уверены, что хотите поставить эту задачу cron в очередь?
|
|
39
39
|
disable: Отключить задачу cron
|
|
40
40
|
enable: Включить задачу cron
|
data/config/routes.rb
CHANGED
|
@@ -17,6 +17,7 @@ GoodJob::Engine.routes.draw do
|
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
get 'jobs/metrics/primary_nav', to: 'metrics#primary_nav', as: :metrics_primary_nav
|
|
20
|
+
get 'jobs/metrics/job_status', to: 'metrics#job_status', as: :metrics_job_status
|
|
20
21
|
|
|
21
22
|
resources :batches, only: %i[index show]
|
|
22
23
|
|
|
@@ -13,6 +13,8 @@ module GoodJob
|
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
ThrottleExceededError = Class.new(ConcurrencyExceededError)
|
|
17
|
+
|
|
16
18
|
module Prepends
|
|
17
19
|
def deserialize(job_data)
|
|
18
20
|
super
|
|
@@ -62,8 +64,13 @@ module GoodJob
|
|
|
62
64
|
total_limit = nil unless total_limit.present? && (0...Float::INFINITY).cover?(total_limit)
|
|
63
65
|
end
|
|
64
66
|
|
|
67
|
+
perform_throttle = job.class.good_job_concurrency_config[:perform_throttle]
|
|
68
|
+
perform_throttle = instance_exec(&perform_throttle) if perform_throttle.respond_to?(:call)
|
|
69
|
+
perform_throttle = nil unless GoodJob::DiscreteExecution.migrated? && perform_throttle.present? && perform_throttle.is_a?(Array) && perform_throttle.size == 2
|
|
70
|
+
|
|
65
71
|
limit = perform_limit || total_limit
|
|
66
|
-
|
|
72
|
+
throttle = perform_throttle
|
|
73
|
+
next unless limit || throttle
|
|
67
74
|
|
|
68
75
|
key = job.good_job_concurrency_key
|
|
69
76
|
next if key.blank?
|
|
@@ -74,9 +81,29 @@ module GoodJob
|
|
|
74
81
|
end
|
|
75
82
|
|
|
76
83
|
GoodJob::Execution.advisory_lock_key(key, function: "pg_advisory_lock") do
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
if limit
|
|
85
|
+
allowed_active_job_ids = GoodJob::Execution.unfinished.where(concurrency_key: key)
|
|
86
|
+
.advisory_locked
|
|
87
|
+
.order(Arel.sql("COALESCE(performed_at, scheduled_at, created_at) ASC"))
|
|
88
|
+
.limit(limit).pluck(:active_job_id)
|
|
89
|
+
# The current job has already been locked and will appear in the previous query
|
|
90
|
+
raise GoodJob::ActiveJobExtensions::Concurrency::ConcurrencyExceededError unless allowed_active_job_ids.include?(job.job_id)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
if throttle
|
|
94
|
+
throttle_limit = throttle[0]
|
|
95
|
+
throttle_period = throttle[1]
|
|
96
|
+
|
|
97
|
+
query = DiscreteExecution.joins(:job)
|
|
98
|
+
.where(GoodJob::Job.table_name => { concurrency_key: key })
|
|
99
|
+
.where(DiscreteExecution.arel_table[:created_at].gt(throttle_period.ago))
|
|
100
|
+
allowed_active_job_ids = query.where(error: nil).or(query.where.not(error: "GoodJob::ActiveJobExtensions::Concurrency::ThrottleExceededError: GoodJob::ActiveJobExtensions::Concurrency::ThrottleExceededError"))
|
|
101
|
+
.order(created_at: :asc)
|
|
102
|
+
.limit(throttle_limit)
|
|
103
|
+
.pluck(:active_job_id)
|
|
104
|
+
|
|
105
|
+
raise ThrottleExceededError unless allowed_active_job_ids.include?(job.job_id)
|
|
106
|
+
end
|
|
80
107
|
end
|
|
81
108
|
end
|
|
82
109
|
end
|
|
@@ -137,23 +164,45 @@ module GoodJob
|
|
|
137
164
|
total_limit = nil unless total_limit.present? && (0...Float::INFINITY).cover?(total_limit)
|
|
138
165
|
end
|
|
139
166
|
|
|
167
|
+
enqueue_throttle = job.class.good_job_concurrency_config[:enqueue_throttle]
|
|
168
|
+
enqueue_throttle = instance_exec(&enqueue_throttle) if enqueue_throttle.respond_to?(:call)
|
|
169
|
+
enqueue_throttle = nil unless enqueue_throttle.present? && enqueue_throttle.is_a?(Array) && enqueue_throttle.size == 2
|
|
170
|
+
|
|
140
171
|
limit = enqueue_limit || total_limit
|
|
141
|
-
|
|
172
|
+
throttle = enqueue_throttle
|
|
173
|
+
return on_enqueue&.call unless limit || throttle
|
|
142
174
|
|
|
143
175
|
GoodJob::Execution.advisory_lock_key(key, function: "pg_advisory_lock") do
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
176
|
+
if limit
|
|
177
|
+
enqueue_concurrency = if enqueue_limit
|
|
178
|
+
GoodJob::Execution.where(concurrency_key: key).unfinished.advisory_unlocked.count
|
|
179
|
+
else
|
|
180
|
+
GoodJob::Execution.where(concurrency_key: key).unfinished.count
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# The job has not yet been enqueued, so check if adding it will go over the limit
|
|
184
|
+
if (enqueue_concurrency + 1) > limit
|
|
185
|
+
logger.info "Aborted enqueue of #{job.class.name} (Job ID: #{job.job_id}) because the concurrency key '#{key}' has reached its enqueue limit of #{limit} #{'job'.pluralize(limit)}"
|
|
186
|
+
on_abort&.call
|
|
187
|
+
break
|
|
188
|
+
end
|
|
156
189
|
end
|
|
190
|
+
|
|
191
|
+
if throttle
|
|
192
|
+
throttle_limit = throttle[0]
|
|
193
|
+
throttle_period = throttle[1]
|
|
194
|
+
enqueued_within_period = GoodJob::Job.where(concurrency_key: key)
|
|
195
|
+
.where(GoodJob::Job.arel_table[:created_at].gt(throttle_period.ago))
|
|
196
|
+
.count
|
|
197
|
+
|
|
198
|
+
if (enqueued_within_period + 1) > throttle_limit
|
|
199
|
+
logger.info "Aborted enqueue of #{job.class.name} (Job ID: #{job.job_id}) because the concurrency key '#{key}' has reached its throttle limit of #{limit} #{'job'.pluralize(limit)}"
|
|
200
|
+
on_abort&.call
|
|
201
|
+
break
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
on_enqueue&.call
|
|
157
206
|
end
|
|
158
207
|
end
|
|
159
208
|
end
|
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.27.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: 2024-03-
|
|
11
|
+
date: 2024-03-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activejob
|
|
@@ -206,6 +206,20 @@ dependencies:
|
|
|
206
206
|
- - ">="
|
|
207
207
|
- !ruby/object:Gem::Version
|
|
208
208
|
version: '0'
|
|
209
|
+
- !ruby/object:Gem::Dependency
|
|
210
|
+
name: timecop
|
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
|
212
|
+
requirements:
|
|
213
|
+
- - ">="
|
|
214
|
+
- !ruby/object:Gem::Version
|
|
215
|
+
version: '0'
|
|
216
|
+
type: :development
|
|
217
|
+
prerelease: false
|
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
219
|
+
requirements:
|
|
220
|
+
- - ">="
|
|
221
|
+
- !ruby/object:Gem::Version
|
|
222
|
+
version: '0'
|
|
209
223
|
- !ruby/object:Gem::Dependency
|
|
210
224
|
name: webrick
|
|
211
225
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -444,7 +458,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
444
458
|
- !ruby/object:Gem::Version
|
|
445
459
|
version: '0'
|
|
446
460
|
requirements: []
|
|
447
|
-
rubygems_version: 3.5.
|
|
461
|
+
rubygems_version: 3.5.4
|
|
448
462
|
signing_key:
|
|
449
463
|
specification_version: 4
|
|
450
464
|
summary: A multithreaded, Postgres-based ActiveJob backend for Ruby on Rails
|