good_job 4.4.1 → 4.5.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 +48 -0
- data/README.md +5 -5
- data/app/charts/good_job/performance_index_chart.rb +3 -0
- data/app/controllers/good_job/cleaner_controller.rb +37 -0
- data/app/controllers/good_job/metrics_controller.rb +2 -0
- data/app/filters/good_job/base_filter.rb +1 -0
- data/app/filters/good_job/jobs_filter.rb +18 -2
- data/app/frontend/good_job/modules/charts.js +12 -0
- data/app/frontend/good_job/modules/html_legend_plugin.js +56 -0
- data/app/frontend/good_job/style.css +13 -0
- data/app/models/good_job/job.rb +6 -1
- data/app/models/good_job/process.rb +31 -1
- data/app/views/good_job/batches/_jobs.erb +5 -4
- data/app/views/good_job/cleaner/index.html.erb +85 -0
- data/app/views/good_job/jobs/_table.erb +3 -2
- data/app/views/good_job/jobs/index.html.erb +1 -1
- data/app/views/good_job/performance/index.html.erb +1 -1
- data/app/views/good_job/performance/show.html.erb +1 -1
- data/app/views/good_job/processes/index.html.erb +1 -0
- data/app/views/good_job/shared/_chart.erb +2 -2
- data/app/views/good_job/shared/_chart_container.erb +21 -0
- data/app/views/good_job/shared/_navbar.erb +6 -0
- data/config/brakeman.ignore +26 -3
- data/config/locales/de.yml +15 -0
- data/config/locales/en.yml +15 -0
- data/config/locales/es.yml +15 -0
- data/config/locales/fr.yml +15 -0
- data/config/locales/it.yml +15 -0
- data/config/locales/ja.yml +15 -0
- data/config/locales/ko.yml +15 -0
- data/config/locales/nl.yml +15 -0
- data/config/locales/pt-BR.yml +15 -0
- data/config/locales/ru.yml +15 -0
- data/config/locales/tr.yml +15 -0
- data/config/locales/uk.yml +15 -0
- data/config/routes.rb +1 -1
- data/lib/good_job/capsule_tracker.rb +2 -19
- data/lib/good_job/cron_manager.rb +24 -12
- data/lib/good_job/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3f0a8926625f33b36c56def63a1507cfb0f8981a876449e907396aa2338b0b78
|
|
4
|
+
data.tar.gz: ad45bd8133d5f68bcf190bac3f7656864e10df2e49be477024e9f0c8422ce222
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2f2e5c4ac4020dfd7aaed53bfb9b7e7480ca91e0aa7cfcbc385ac60d91a4b08e40397734993ffdffc9d5121f40afab04540d363fef925bf8a30bf987938514ab
|
|
7
|
+
data.tar.gz: 3460da22048d4fad135e12644c043723b2dcba8137a69a3b5bebf3b5df11e1c1a0ed0f94a8924b64cab4d1ce8b76bd55e6090a25c43f23afde41b27d398cf59a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v4.5.0](https://github.com/bensheldon/good_job/tree/v4.5.0) (2024-11-22)
|
|
4
|
+
|
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.4.2...v4.5.0)
|
|
6
|
+
|
|
7
|
+
**Implemented enhancements:**
|
|
8
|
+
|
|
9
|
+
- Add "Discard Cleaner" page to dashboard UI [\#1538](https://github.com/bensheldon/good_job/pull/1538) ([lucasfcunha](https://github.com/lucasfcunha))
|
|
10
|
+
- Add Process memory usage and fix process state update [\#1516](https://github.com/bensheldon/good_job/pull/1516) ([noma4i](https://github.com/noma4i))
|
|
11
|
+
|
|
12
|
+
**Fixed bugs:**
|
|
13
|
+
|
|
14
|
+
- Fix cron double-enqueue because delay close to 0.01 and possibly clock-drift [\#1543](https://github.com/bensheldon/good_job/pull/1543) ([ccouton](https://github.com/ccouton))
|
|
15
|
+
- Fix badge color for running jobs [\#1525](https://github.com/bensheldon/good_job/pull/1525) ([Wittiest](https://github.com/Wittiest))
|
|
16
|
+
|
|
17
|
+
**Closed issues:**
|
|
18
|
+
|
|
19
|
+
- Can't load Dashboard [\#1532](https://github.com/bensheldon/good_job/issues/1532)
|
|
20
|
+
- Should we clean up batches if discarded callback jobs exist? [\#1528](https://github.com/bensheldon/good_job/issues/1528)
|
|
21
|
+
- Modify error color for Running tab when a job has 1 attempt [\#1518](https://github.com/bensheldon/good_job/issues/1518)
|
|
22
|
+
- Silence development warning output [\#1509](https://github.com/bensheldon/good_job/issues/1509)
|
|
23
|
+
- Proposal - A better way of managing errors through the GoodJob UI [\#1464](https://github.com/bensheldon/good_job/issues/1464)
|
|
24
|
+
|
|
25
|
+
**Merged pull requests:**
|
|
26
|
+
|
|
27
|
+
- Ignore some warnings with the `warning` gem [\#1545](https://github.com/bensheldon/good_job/pull/1545) ([Earlopain](https://github.com/Earlopain))
|
|
28
|
+
- Remove unneeded include of pg\_locks in query when displaying jobs table [\#1541](https://github.com/bensheldon/good_job/pull/1541) ([jgrau](https://github.com/jgrau))
|
|
29
|
+
- Update development environment to Rails 8 [\#1539](https://github.com/bensheldon/good_job/pull/1539) ([bensheldon](https://github.com/bensheldon))
|
|
30
|
+
- Bump the bundler-dependencies group with 9 updates [\#1534](https://github.com/bensheldon/good_job/pull/1534) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
31
|
+
- Bump the bundler-lint group with 5 updates [\#1533](https://github.com/bensheldon/good_job/pull/1533) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
32
|
+
- Bump rexml from 3.3.8 to 3.3.9 [\#1530](https://github.com/bensheldon/good_job/pull/1530) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
33
|
+
- Deprecate GoodJob::Job\#recent\_error [\#1526](https://github.com/bensheldon/good_job/pull/1526) ([Wittiest](https://github.com/Wittiest))
|
|
34
|
+
|
|
35
|
+
## [v4.4.2](https://github.com/bensheldon/good_job/tree/v4.4.2) (2024-10-18)
|
|
36
|
+
|
|
37
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.4.1...v4.4.2)
|
|
38
|
+
|
|
39
|
+
**Merged pull requests:**
|
|
40
|
+
|
|
41
|
+
- Bump rails from 7.1.4 to 7.1.4.1 [\#1524](https://github.com/bensheldon/good_job/pull/1524) ([Earlopain](https://github.com/Earlopain))
|
|
42
|
+
- Drop compatibility code for `ForkTracker` [\#1519](https://github.com/bensheldon/good_job/pull/1519) ([Earlopain](https://github.com/Earlopain))
|
|
43
|
+
- Add vertical legend to performance dashboard [\#1517](https://github.com/bensheldon/good_job/pull/1517) ([Wittiest](https://github.com/Wittiest))
|
|
44
|
+
- Bump the bundler-dependencies group across 1 directory with 10 updates [\#1515](https://github.com/bensheldon/good_job/pull/1515) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
45
|
+
- Manually bump tapioca / regenerate rbi [\#1514](https://github.com/bensheldon/good_job/pull/1514) ([Earlopain](https://github.com/Earlopain))
|
|
46
|
+
- Remove rack from the gemfile [\#1512](https://github.com/bensheldon/good_job/pull/1512) ([Earlopain](https://github.com/Earlopain))
|
|
47
|
+
- Add regression test for searchable numeric arguments [\#1510](https://github.com/bensheldon/good_job/pull/1510) ([bensheldon](https://github.com/bensheldon))
|
|
48
|
+
- Better parallelize CI jobs [\#1507](https://github.com/bensheldon/good_job/pull/1507) ([Earlopain](https://github.com/Earlopain))
|
|
49
|
+
- Bump webrick from 1.8.1 to 1.8.2 [\#1503](https://github.com/bensheldon/good_job/pull/1503) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
50
|
+
|
|
3
51
|
## [v4.4.1](https://github.com/bensheldon/good_job/tree/v4.4.1) (2024-10-10)
|
|
4
52
|
|
|
5
53
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.4.0...v4.4.1)
|
data/README.md
CHANGED
|
@@ -1715,14 +1715,14 @@ Environment variables that may help with debugging:
|
|
|
1715
1715
|
- `LOUD=1`: display all stdout/stderr output from all sources. This is helpful because GoodJob wraps some tests with `quiet { }` for cleaner test output, but it can hinder debugging.
|
|
1716
1716
|
- `SHOW_BROWSER=1`: Run system tests headfully with Chrome/Chromedriver. Use `binding.irb` in the system tests to pause.
|
|
1717
1717
|
|
|
1718
|
-
|
|
1718
|
+
The gemfiles in `gemfiles/` can be used to run tests against different rails versions:
|
|
1719
1719
|
|
|
1720
1720
|
```bash
|
|
1721
|
-
# Install
|
|
1722
|
-
|
|
1721
|
+
# Install dependencies
|
|
1722
|
+
BUNDLE_GEMFILE=gemfiles/rails_6.1.gemfile bundle install
|
|
1723
1723
|
|
|
1724
|
-
# Run tests
|
|
1725
|
-
|
|
1724
|
+
# Run the tests
|
|
1725
|
+
BUNDLE_GEMFILE=gemfiles/rails_6.1.gemfile bin/rspec
|
|
1726
1726
|
```
|
|
1727
1727
|
|
|
1728
1728
|
### Release
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GoodJob
|
|
4
|
+
class CleanerController < ApplicationController
|
|
5
|
+
def index
|
|
6
|
+
@filter = JobsFilter.new(params)
|
|
7
|
+
|
|
8
|
+
@discarded_jobs_grouped_by_exception =
|
|
9
|
+
GoodJob::Job.discarded
|
|
10
|
+
.select(<<-SQL.squish)
|
|
11
|
+
SPLIT_PART(error, ': ', 1) AS exception_class,
|
|
12
|
+
count(id) AS failed,
|
|
13
|
+
COUNT(id) FILTER (WHERE "finished_at" > NOW() - INTERVAL '1 HOUR') AS last_1_hour,
|
|
14
|
+
COUNT(id) FILTER (WHERE "finished_at" > NOW() - INTERVAL '3 HOURS') AS last_3_hours,
|
|
15
|
+
COUNT(id) FILTER (WHERE "finished_at" > NOW() - INTERVAL '24 HOURS') AS last_24_hours,
|
|
16
|
+
COUNT(id) FILTER (WHERE "finished_at" > NOW() - INTERVAL '3 DAYS') AS last_3_days,
|
|
17
|
+
COUNT(id) FILTER (WHERE "finished_at" > NOW() - INTERVAL '7 DAYS') AS last_7_days
|
|
18
|
+
SQL
|
|
19
|
+
.order(:exception_class)
|
|
20
|
+
.group(:exception_class)
|
|
21
|
+
|
|
22
|
+
@discarded_jobs_grouped_by_class =
|
|
23
|
+
GoodJob::Job.discarded
|
|
24
|
+
.select(<<-SQL.squish)
|
|
25
|
+
job_class,
|
|
26
|
+
count(id) AS failed,
|
|
27
|
+
COUNT(*) FILTER (WHERE "finished_at" > NOW() - INTERVAL '1 HOUR') AS last_1_hour,
|
|
28
|
+
COUNT(*) FILTER (WHERE "finished_at" > NOW() - INTERVAL '3 HOURS') AS last_3_hours,
|
|
29
|
+
COUNT(*) FILTER (WHERE "finished_at" > NOW() - INTERVAL '24 HOURS') AS last_24_hours,
|
|
30
|
+
COUNT(*) FILTER (WHERE "finished_at" > NOW() - INTERVAL '3 DAYS') AS last_3_days,
|
|
31
|
+
COUNT(*) FILTER (WHERE "finished_at" > NOW() - INTERVAL '7 DAYS') AS last_7_days
|
|
32
|
+
SQL
|
|
33
|
+
.order(:job_class)
|
|
34
|
+
.group(:job_class)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -7,12 +7,14 @@ module GoodJob
|
|
|
7
7
|
batches_count = GoodJob::BatchRecord.all.size
|
|
8
8
|
cron_entries_count = GoodJob::CronEntry.all.size
|
|
9
9
|
processes_count = GoodJob::Process.active.count
|
|
10
|
+
discarded_count = GoodJob::Job.discarded.count
|
|
10
11
|
|
|
11
12
|
render json: {
|
|
12
13
|
jobs_count: helpers.number_to_human(jobs_count),
|
|
13
14
|
batches_count: helpers.number_to_human(batches_count),
|
|
14
15
|
cron_entries_count: helpers.number_to_human(cron_entries_count),
|
|
15
16
|
processes_count: helpers.number_to_human(processes_count),
|
|
17
|
+
discarded_count: helpers.number_to_human(discarded_count),
|
|
16
18
|
}
|
|
17
19
|
end
|
|
18
20
|
|
|
@@ -27,6 +27,7 @@ module GoodJob
|
|
|
27
27
|
query = query.where(queue_name: filter_params[:queue_name]) if filter_params[:queue_name].present?
|
|
28
28
|
query = query.search_text(filter_params[:query]) if filter_params[:query].present?
|
|
29
29
|
query = query.where(cron_key: filter_params[:cron_key]) if filter_params[:cron_key].present?
|
|
30
|
+
query = query.where(finished_at: finished_since(filter_params[:finished_since])..) if filter_params[:finished_since].present?
|
|
30
31
|
|
|
31
32
|
if filter_params[:state]
|
|
32
33
|
case filter_params[:state]
|
|
@@ -39,7 +40,7 @@ module GoodJob
|
|
|
39
40
|
when 'scheduled'
|
|
40
41
|
query = query.scheduled
|
|
41
42
|
when 'running'
|
|
42
|
-
query = query.running
|
|
43
|
+
query = query.running
|
|
43
44
|
when 'queued'
|
|
44
45
|
query = query.queued
|
|
45
46
|
end
|
|
@@ -55,11 +56,26 @@ module GoodJob
|
|
|
55
56
|
private
|
|
56
57
|
|
|
57
58
|
def query_for_records
|
|
58
|
-
filtered_query
|
|
59
|
+
filtered_query
|
|
59
60
|
end
|
|
60
61
|
|
|
61
62
|
def default_base_query
|
|
62
63
|
GoodJob::Job.all
|
|
63
64
|
end
|
|
65
|
+
|
|
66
|
+
def finished_since(finished_since)
|
|
67
|
+
case finished_since
|
|
68
|
+
when '1_hour_ago'
|
|
69
|
+
1.hour.ago
|
|
70
|
+
when '3_hours_ago'
|
|
71
|
+
3.hours.ago
|
|
72
|
+
when '24_hours_ago'
|
|
73
|
+
24.hours.ago
|
|
74
|
+
when '3_days_ago'
|
|
75
|
+
3.days.ago
|
|
76
|
+
when '7_days_ago'
|
|
77
|
+
7.days.ago
|
|
78
|
+
end
|
|
79
|
+
end
|
|
64
80
|
end
|
|
65
81
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import htmlLegendPlugin from "html_legend_plugin";
|
|
2
|
+
|
|
1
3
|
function renderCharts(animate) {
|
|
2
4
|
const charts = document.querySelectorAll('.chart');
|
|
3
5
|
|
|
@@ -5,6 +7,16 @@ function renderCharts(animate) {
|
|
|
5
7
|
const chartEl = charts[i];
|
|
6
8
|
const chartData = JSON.parse(chartEl.dataset.json);
|
|
7
9
|
chartData.options ||= {};
|
|
10
|
+
|
|
11
|
+
if (chartData.options.plugins?.legend?.vertical) {
|
|
12
|
+
chartData.plugins = [htmlLegendPlugin];
|
|
13
|
+
chartData.options.plugins = {
|
|
14
|
+
...chartData.options.plugins,
|
|
15
|
+
legend: {
|
|
16
|
+
display: false,
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
8
20
|
chartData.options.animation = animate;
|
|
9
21
|
chartData.options.responsive = true;
|
|
10
22
|
chartData.options.maintainAspectRatio = false;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const generateListItem = (item) => {
|
|
2
|
+
const li = document.createElement('li');
|
|
3
|
+
li.className = 'd-flex align-items-center text-nowrap mb-2';
|
|
4
|
+
|
|
5
|
+
const boxSpan = document.createElement('span');
|
|
6
|
+
boxSpan.className = 'legend-item-color-box';
|
|
7
|
+
boxSpan.style.background = item.fillStyle;
|
|
8
|
+
boxSpan.style.borderColor = item.strokeStyle;
|
|
9
|
+
boxSpan.style.borderWidth = item.lineWidth + 'px';
|
|
10
|
+
|
|
11
|
+
const textContainer = document.createElement('p');
|
|
12
|
+
textContainer.className = 'item-text m-0 small';
|
|
13
|
+
textContainer.style.color = item.fontColor;
|
|
14
|
+
textContainer.style.textDecoration = item.hidden ? 'line-through' : '';
|
|
15
|
+
|
|
16
|
+
const text = document.createTextNode(item.text);
|
|
17
|
+
textContainer.appendChild(text);
|
|
18
|
+
|
|
19
|
+
li.appendChild(boxSpan);
|
|
20
|
+
li.appendChild(textContainer);
|
|
21
|
+
|
|
22
|
+
return li;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const htmlLegendPlugin = {
|
|
26
|
+
id: 'htmlLegend',
|
|
27
|
+
afterUpdate(chart, _args, _options) {
|
|
28
|
+
const {type} = chart.config;
|
|
29
|
+
const ul = document.getElementById('chart-legend-ul');
|
|
30
|
+
|
|
31
|
+
// Remove old legend items
|
|
32
|
+
while (ul.firstChild) {
|
|
33
|
+
ul.firstChild.remove();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Reuse the built-in legendItems generator
|
|
37
|
+
const items = chart.options.plugins.legend.labels.generateLabels(chart);
|
|
38
|
+
|
|
39
|
+
items.forEach(item => {
|
|
40
|
+
const li = generateListItem(item);
|
|
41
|
+
ul.appendChild(li);
|
|
42
|
+
|
|
43
|
+
li.onclick = () => {
|
|
44
|
+
if (type === 'pie' || type === 'doughnut') {
|
|
45
|
+
// Pie and doughnut charts only have a single dataset and visibility is per item
|
|
46
|
+
chart.toggleDataVisibility(item.index);
|
|
47
|
+
} else {
|
|
48
|
+
chart.setDatasetVisibility(item.datasetIndex, !chart.isDatasetVisible(item.datasetIndex));
|
|
49
|
+
}
|
|
50
|
+
chart.update();
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export { htmlLegendPlugin as default };
|
|
@@ -24,6 +24,19 @@
|
|
|
24
24
|
height: 200px;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
.legend-item-color-box {
|
|
28
|
+
display: inline-block;
|
|
29
|
+
flex-shrink: 0;
|
|
30
|
+
height: 20px;
|
|
31
|
+
width: 20px;
|
|
32
|
+
border-style: solid;
|
|
33
|
+
margin-right: 3px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#chart-legend-container {
|
|
37
|
+
height: 200px;
|
|
38
|
+
}
|
|
39
|
+
|
|
27
40
|
/* Break out of a container */
|
|
28
41
|
.break-out {
|
|
29
42
|
width:100vw;
|
data/app/models/good_job/job.rb
CHANGED
|
@@ -390,7 +390,12 @@ module GoodJob
|
|
|
390
390
|
# If the job has been retried, the error will be fetched from the previous {Execution} record.
|
|
391
391
|
# @return [String]
|
|
392
392
|
def recent_error
|
|
393
|
-
|
|
393
|
+
GoodJob.deprecator.warn(<<~DEPRECATION)
|
|
394
|
+
The `GoodJob::Job#recent_error` method is deprecated and will be removed in the next major release.
|
|
395
|
+
|
|
396
|
+
Replace usage of GoodJob::Job#recent_error with `GoodJob::Job#error`.
|
|
397
|
+
DEPRECATION
|
|
398
|
+
error
|
|
394
399
|
end
|
|
395
400
|
|
|
396
401
|
# Errors for the job to be displayed in the Dashboard.
|
|
@@ -12,6 +12,28 @@ module GoodJob # :nodoc:
|
|
|
12
12
|
STALE_INTERVAL = 30.seconds
|
|
13
13
|
# Interval until the process record is treated as expired
|
|
14
14
|
EXPIRED_INTERVAL = 5.minutes
|
|
15
|
+
PROCESS_MEMORY = case RUBY_PLATFORM
|
|
16
|
+
when /linux/
|
|
17
|
+
lambda do |pid|
|
|
18
|
+
File.readlines("/proc/#{pid}/smaps_rollup").each do |line|
|
|
19
|
+
next unless line.start_with?('Pss:')
|
|
20
|
+
|
|
21
|
+
break line.split[1].to_i
|
|
22
|
+
end
|
|
23
|
+
rescue Errno::ENOENT
|
|
24
|
+
File.readlines("/proc/#{pid}/status").each do |line|
|
|
25
|
+
next unless line.start_with?('VmRSS:')
|
|
26
|
+
|
|
27
|
+
break line.split[1].to_i
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
when /darwin|bsd/
|
|
31
|
+
lambda do |pid|
|
|
32
|
+
`ps -o pid,rss -p #{pid.to_i}`.lines.last.split.last.to_i
|
|
33
|
+
end
|
|
34
|
+
else
|
|
35
|
+
->(_pid) { 0 }
|
|
36
|
+
end
|
|
15
37
|
|
|
16
38
|
self.table_name = 'good_job_processes'
|
|
17
39
|
self.implicit_order_column = 'created_at'
|
|
@@ -56,6 +78,13 @@ module GoodJob # :nodoc:
|
|
|
56
78
|
end
|
|
57
79
|
end
|
|
58
80
|
|
|
81
|
+
# @return [Integer]
|
|
82
|
+
def self.memory_usage(pid)
|
|
83
|
+
PROCESS_MEMORY.call(pid)
|
|
84
|
+
rescue StandardError
|
|
85
|
+
0
|
|
86
|
+
end
|
|
87
|
+
|
|
59
88
|
def self.find_or_create_record(id:, with_advisory_lock: false)
|
|
60
89
|
attributes = {
|
|
61
90
|
id: id,
|
|
@@ -83,6 +112,7 @@ module GoodJob # :nodoc:
|
|
|
83
112
|
{
|
|
84
113
|
hostname: Socket.gethostname,
|
|
85
114
|
pid: ::Process.pid,
|
|
115
|
+
memory: memory_usage(::Process.pid),
|
|
86
116
|
proctitle: $PROGRAM_NAME,
|
|
87
117
|
preserve_job_records: GoodJob.preserve_job_records,
|
|
88
118
|
retry_on_unhandled_error: GoodJob.retry_on_unhandled_error,
|
|
@@ -98,8 +128,8 @@ module GoodJob # :nodoc:
|
|
|
98
128
|
end
|
|
99
129
|
|
|
100
130
|
def refresh
|
|
101
|
-
self.state = self.class.process_state
|
|
102
131
|
reload # verify the record still exists in the database
|
|
132
|
+
self.state = self.class.process_state
|
|
103
133
|
update(state: state, updated_at: Time.current)
|
|
104
134
|
rescue ActiveRecord::RecordNotFound
|
|
105
135
|
@new_record = true
|
|
@@ -35,17 +35,18 @@
|
|
|
35
35
|
</div>
|
|
36
36
|
<div class="col-4 col-lg-1 text-lg-end">
|
|
37
37
|
<div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.attempts" %></div>
|
|
38
|
-
<% if job.
|
|
38
|
+
<% if job.error %>
|
|
39
39
|
<%= tag.span job.executions_count, class: "badge rounded-pill bg-danger",
|
|
40
40
|
data: {
|
|
41
41
|
bs_toggle: "popover",
|
|
42
42
|
bs_trigger: "hover focus click",
|
|
43
43
|
bs_placement: "bottom",
|
|
44
|
-
bs_content: job.
|
|
44
|
+
bs_content: job.display_error,
|
|
45
45
|
}
|
|
46
46
|
%>
|
|
47
47
|
<% else %>
|
|
48
|
-
|
|
48
|
+
<% executions_badge_color = job.executions_count > 1 ? "bg-warning" : "bg-secondary" %>
|
|
49
|
+
<span class="badge rounded-pill <%= executions_badge_color %>"><%= job.executions_count %></span>
|
|
49
50
|
<% end %>
|
|
50
51
|
</div>
|
|
51
52
|
<div class="mt-3 mt-lg-0 col d-flex gap-3 align-items-center justify-content-end">
|
|
@@ -79,7 +80,7 @@
|
|
|
79
80
|
<% end %>
|
|
80
81
|
</li>
|
|
81
82
|
<li>
|
|
82
|
-
<%= link_to job_path(job.id), method: :delete, class: "dropdown-item #{'disabled' unless job.
|
|
83
|
+
<%= link_to job_path(job.id), method: :delete, class: "dropdown-item #{'disabled' unless job.finished?}", title: t(".actions.destroy"), data: { confirm: t(".actions.confirm_destroy"), disable: true } do %>
|
|
83
84
|
<%= render_icon "trash" %>
|
|
84
85
|
<%= t "good_job.actions.destroy" %>
|
|
85
86
|
<% end %>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<div class="border-bottom">
|
|
2
|
+
<h2 class="pt-3 pb-2"><%= t ".title" %></h2>
|
|
3
|
+
</div>
|
|
4
|
+
|
|
5
|
+
<div>
|
|
6
|
+
<h4 class="pt-3 pb-2"><%= t ".grouped_by_class" %></h4>
|
|
7
|
+
<table class="table align-middle" id="by-job-class">
|
|
8
|
+
<thead>
|
|
9
|
+
<tr>
|
|
10
|
+
<th scope="col" class="col-3"><%= t ".class" %></th>
|
|
11
|
+
<th scope="col" class="text-center"><%= t ".all" %></th>
|
|
12
|
+
<th scope="col" class="text-center"><%= t ".last_1_hour" %></th>
|
|
13
|
+
<th scope="col" class="text-center"><%= t ".last_3_hours" %></th>
|
|
14
|
+
<th scope="col" class="text-center"><%= t ".last_24_hours" %></th>
|
|
15
|
+
<th scope="col" class="text-center"><%= t ".last_3_days" %></th>
|
|
16
|
+
<th scope="col" class="text-center"><%= t ".last_7_days" %></th>
|
|
17
|
+
</tr>
|
|
18
|
+
</thead>
|
|
19
|
+
<tbody>
|
|
20
|
+
<% @discarded_jobs_grouped_by_class.each do |discard_job| %>
|
|
21
|
+
<tr>
|
|
22
|
+
<td scope="row" class="col-3 text-break"><%= discard_job.job_class %></td>
|
|
23
|
+
<td class="text-center "><%= link_to discard_job.failed, jobs_path(@filter.to_params(job_class: discard_job.job_class, state: 'discarded')) %></td>
|
|
24
|
+
<td class="text-center"><%= link_to discard_job.last_1_hour, jobs_path(@filter.to_params(job_class: discard_job.job_class, state: 'discarded', finished_since: '1_hour_ago')) %></td>
|
|
25
|
+
<td class="text-center"><%= link_to discard_job.last_3_hours, jobs_path(@filter.to_params(job_class: discard_job.job_class, state: 'discarded', finished_since: '3_hours_ago')) %></td>
|
|
26
|
+
<td class="text-center"><%= link_to discard_job.last_24_hours, jobs_path(@filter.to_params(job_class: discard_job.job_class, state: 'discarded', finished_since: '24_hours_ago')) %></td>
|
|
27
|
+
<td class="text-center"><%= link_to discard_job.last_3_days, jobs_path(@filter.to_params(job_class: discard_job.job_class, state: 'discarded', finished_since: '3_days_ago')) %></td>
|
|
28
|
+
<td class="text-center"><%= link_to discard_job.last_7_days, jobs_path(@filter.to_params(job_class: discard_job.job_class, state: 'discarded', finished_since: '7_days_ago')) %></td>
|
|
29
|
+
</tr>
|
|
30
|
+
<% end %>
|
|
31
|
+
</tbody>
|
|
32
|
+
<tfoot>
|
|
33
|
+
<tr>
|
|
34
|
+
<td scope="row" class="col-3 fw-bold"><%= t ".total" %></td>
|
|
35
|
+
<td class="text-center fw-bold"><%= link_to @discarded_jobs_grouped_by_class.sum(&:failed), jobs_path(@filter.to_params(state: 'discarded')) %></td>
|
|
36
|
+
<td class="text-center fw-bold"><%= link_to @discarded_jobs_grouped_by_class.sum(&:last_1_hour), jobs_path(@filter.to_params(state: 'discarded', finished_since: '1_hour_ago')) %></td>
|
|
37
|
+
<td class="text-center fw-bold"><%= link_to @discarded_jobs_grouped_by_class.sum(&:last_3_hours), jobs_path(@filter.to_params(state: 'discarded', finished_since: '3_hours_ago')) %></td>
|
|
38
|
+
<td class="text-center fw-bold"><%= link_to @discarded_jobs_grouped_by_class.sum(&:last_24_hours), jobs_path(@filter.to_params(state: 'discarded', finished_since: '24_hours_ago')) %></td>
|
|
39
|
+
<td class="text-center fw-bold"><%= link_to @discarded_jobs_grouped_by_class.sum(&:last_3_days), jobs_path(@filter.to_params(state: 'discarded', finished_since: '3_days_ago')) %></td>
|
|
40
|
+
<td class="text-center fw-bold"><%= link_to @discarded_jobs_grouped_by_class.sum(&:last_7_days), jobs_path(@filter.to_params(state: 'discarded', finished_since: '7_days_ago')) %></td>
|
|
41
|
+
</tr>
|
|
42
|
+
</tfoot>
|
|
43
|
+
</table>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div>
|
|
47
|
+
<h4 class="pt-3 pb-2"><%= t ".grouped_by_exception" %></h4>
|
|
48
|
+
<table class="table align-middle" id="by-exception">
|
|
49
|
+
<thead>
|
|
50
|
+
<tr>
|
|
51
|
+
<th scope="col" class="col-3"><%= t ".exception" %></th>
|
|
52
|
+
<th scope="col" class="text-center"><%= t ".all" %></th>
|
|
53
|
+
<th scope="col" class="text-center"><%= t ".last_1_hour" %></th>
|
|
54
|
+
<th scope="col" class="text-center"><%= t ".last_3_hours" %></th>
|
|
55
|
+
<th scope="col" class="text-center"><%= t ".last_24_hours" %></th>
|
|
56
|
+
<th scope="col" class="text-center"><%= t ".last_3_days" %></th>
|
|
57
|
+
<th scope="col" class="text-center"><%= t ".last_7_days" %></th>
|
|
58
|
+
</tr>
|
|
59
|
+
</thead>
|
|
60
|
+
<tbody>
|
|
61
|
+
<% @discarded_jobs_grouped_by_exception.each do |discard_job| %>
|
|
62
|
+
<tr>
|
|
63
|
+
<td scope="row" class="col-3 text-break"><%= discard_job.exception_class %></td>
|
|
64
|
+
<td class="text-center"><%= link_to discard_job.failed, jobs_path(@filter.to_params(state: 'discarded', query: discard_job.exception_class)) %></td>
|
|
65
|
+
<td class="text-center"><%= link_to discard_job.last_1_hour, jobs_path(@filter.to_params(state: 'discarded', query: discard_job.exception_class, finished_since: '1_hour_ago')) %></td>
|
|
66
|
+
<td class="text-center"><%= link_to discard_job.last_3_hours, jobs_path(@filter.to_params(state: 'discarded', query: discard_job.exception_class, finished_since: '3_hours_ago')) %></td>
|
|
67
|
+
<td class="text-center"><%= link_to discard_job.last_24_hours, jobs_path(@filter.to_params(state: 'discarded', query: discard_job.exception_class, finished_since: '24_hours_ago')) %></td>
|
|
68
|
+
<td class="text-center"><%= link_to discard_job.last_3_days, jobs_path(@filter.to_params(state: 'discarded', query: discard_job.exception_class, finished_since: '3_days_ago')) %></td>
|
|
69
|
+
<td class="text-center"><%= link_to discard_job.last_7_days, jobs_path(@filter.to_params(state: 'discarded', query: discard_job.exception_class, finished_since: '7_days_ago')) %></td>
|
|
70
|
+
</tr>
|
|
71
|
+
<% end %>
|
|
72
|
+
</tbody>
|
|
73
|
+
<tfoot>
|
|
74
|
+
<tr>
|
|
75
|
+
<td scope="row" class="col-3 fw-bold"><%= t ".total" %></td>
|
|
76
|
+
<td class="text-center fw-bold"><%= link_to @discarded_jobs_grouped_by_exception.sum(&:failed), jobs_path(@filter.to_params(state: 'discarded')) %></td>
|
|
77
|
+
<td class="text-center fw-bold"><%= link_to @discarded_jobs_grouped_by_exception.sum(&:last_1_hour), jobs_path(@filter.to_params(state: 'discarded', finished_since: '1_hour_ago')) %></td>
|
|
78
|
+
<td class="text-center fw-bold"><%= link_to @discarded_jobs_grouped_by_exception.sum(&:last_3_hours), jobs_path(@filter.to_params(state: 'discarded', finished_since: '3_hours_ago')) %></td>
|
|
79
|
+
<td class="text-center fw-bold"><%= link_to @discarded_jobs_grouped_by_exception.sum(&:last_24_hours), jobs_path(@filter.to_params(state: 'discarded', finished_since: '24_hours_ago')) %></td>
|
|
80
|
+
<td class="text-center fw-bold"><%= link_to @discarded_jobs_grouped_by_exception.sum(&:last_3_days), jobs_path(@filter.to_params(state: 'discarded', finished_since: '3_days_ago')) %></td>
|
|
81
|
+
<td class="text-center fw-bold"><%= link_to @discarded_jobs_grouped_by_exception.sum(&:last_7_days), jobs_path(@filter.to_params(state: 'discarded', finished_since: '7_days_ago')) %></td>
|
|
82
|
+
</tr>
|
|
83
|
+
</tfoot>
|
|
84
|
+
</table>
|
|
85
|
+
</div>
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
</div>
|
|
85
85
|
<div class="col-4 col-lg-1 text-lg-end">
|
|
86
86
|
<div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.attempts" %></div>
|
|
87
|
-
<% if job.
|
|
87
|
+
<% if job.error %>
|
|
88
88
|
<%= tag.span job.executions_count, class: "badge rounded-pill bg-danger",
|
|
89
89
|
data: {
|
|
90
90
|
bs_toggle: "popover",
|
|
@@ -94,7 +94,8 @@
|
|
|
94
94
|
}
|
|
95
95
|
%>
|
|
96
96
|
<% else %>
|
|
97
|
-
|
|
97
|
+
<% executions_badge_color = job.executions_count > 1 ? "bg-warning" : "bg-secondary" %>
|
|
98
|
+
<span class="badge rounded-pill <%= executions_badge_color %>"><%= job.executions_count %></span>
|
|
98
99
|
<% end %>
|
|
99
100
|
</div>
|
|
100
101
|
<div class="mt-3 mt-lg-0 col">
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<%= render 'good_job/shared/filter', title: t("good_job.shared.navbar.jobs"), filter: @filter %>
|
|
2
|
-
<%= render 'good_job/shared/
|
|
2
|
+
<%= render 'good_job/shared/chart_container', chart_data: GoodJob::ScheduledByQueueChart.new(@filter).data %>
|
|
3
3
|
|
|
4
4
|
<div data-live-poll-region="jobs-table">
|
|
5
5
|
<%= render 'good_job/jobs/table', jobs: @filter.records, filter: @filter %>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<h2 class="pt-3 pb-2"><%= t ".title" %></h2>
|
|
3
3
|
</div>
|
|
4
4
|
|
|
5
|
-
<%= render 'good_job/shared/
|
|
5
|
+
<%= render 'good_job/shared/chart_container', chart_data: GoodJob::PerformanceIndexChart.new.data %>
|
|
6
6
|
|
|
7
7
|
<div class="my-3 card">
|
|
8
8
|
<div class="list-group list-group-flush text-nowrap" role="table">
|
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
<h2 class="pt-3 pb-2"><%= t ".title" %> - <%= @job_class %></h2>
|
|
3
3
|
</div>
|
|
4
4
|
|
|
5
|
-
<%= render 'good_job/shared/
|
|
5
|
+
<%= render 'good_job/shared/chart_container', chart_data: GoodJob::PerformanceShowChart.new(@job_class).data %>
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
<span class="badge rounded-pill bg-body-secondary text-secondary"><%= process.state["pid"] %></span>
|
|
39
39
|
<span class="text-muted small">@</span>
|
|
40
40
|
<span class="badge rounded-pill bg-body-secondary text-secondary"><%= process.state["hostname"] %></span>
|
|
41
|
+
<span class="badge rounded-pill bg-body-secondary text-secondary"><%= (process.state["memory"] / 1024).to_i %> MB</span>
|
|
41
42
|
</div>
|
|
42
43
|
</div>
|
|
43
44
|
<div class="col">
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<% vertical_legend = chart_data.dig(:options, :plugins, :legend, :vertical) %>
|
|
2
|
+
|
|
3
|
+
<div class="container-fluid">
|
|
4
|
+
<div class="row d-flex">
|
|
5
|
+
<% if vertical_legend %>
|
|
6
|
+
<div class="row d-flex">
|
|
7
|
+
<div class="col-md-10">
|
|
8
|
+
<%= render 'good_job/shared/chart', chart_data: chart_data %>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="col-md-2 d-flex flex-column">
|
|
11
|
+
<div id="chart-legend-container" class="flex-fill overflow-auto">
|
|
12
|
+
<ul id="chart-legend-ul" class="list-unstyled p-0 mx-0 py-2">
|
|
13
|
+
</ul>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
<% else %>
|
|
18
|
+
<%= render 'good_job/shared/chart', chart_data: chart_data %>
|
|
19
|
+
<% end %>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
@@ -44,6 +44,12 @@
|
|
|
44
44
|
<%= t(".performance") %>
|
|
45
45
|
<% end %>
|
|
46
46
|
</li>
|
|
47
|
+
<li class="nav-item">
|
|
48
|
+
<%= link_to cleaner_index_path, class: ["nav-link", ("active" if controller_name == 'cleaner')] do %>
|
|
49
|
+
<%= t(".cleaner") %>
|
|
50
|
+
<span data-async-values-target="value" data-async-values-key="discarded_count" class="badge bg-secondary rounded-pill d-none"></span>
|
|
51
|
+
<% end %>
|
|
52
|
+
</li>
|
|
47
53
|
</ul>
|
|
48
54
|
|
|
49
55
|
<ul class="navbar-nav">
|
data/config/brakeman.ignore
CHANGED
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"check_name": "SQL",
|
|
54
54
|
"message": "Possible SQL injection",
|
|
55
55
|
"file": "app/models/good_job/job.rb",
|
|
56
|
-
"line":
|
|
56
|
+
"line": 135,
|
|
57
57
|
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
|
|
58
58
|
"code": "Arel.sql(\"(CASE #{queues.map.with_index do\n sanitize_sql_array([\"WHEN queue_name = ? THEN ?\", queue_name, index])\n end.join(\" \")} ELSE #{queues.size} END)\")",
|
|
59
59
|
"render_path": null,
|
|
@@ -68,8 +68,31 @@
|
|
|
68
68
|
89
|
|
69
69
|
],
|
|
70
70
|
"note": "Developer provided value, queue_name, is sanitized."
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"warning_type": "Command Injection",
|
|
74
|
+
"warning_code": 14,
|
|
75
|
+
"fingerprint": "dbb80167f57edebfd9da72e1278d425095a0329755e24d3c50e0fda6bb21c097",
|
|
76
|
+
"check_name": "Execute",
|
|
77
|
+
"message": "Possible command injection",
|
|
78
|
+
"file": "app/models/good_job/process.rb",
|
|
79
|
+
"line": 32,
|
|
80
|
+
"link": "https://brakemanscanner.org/docs/warning_types/command_injection/",
|
|
81
|
+
"code": "`ps -o pid,rss -p #{pid.to_i}`",
|
|
82
|
+
"render_path": null,
|
|
83
|
+
"location": {
|
|
84
|
+
"type": "method",
|
|
85
|
+
"class": "Process",
|
|
86
|
+
"method": null
|
|
87
|
+
},
|
|
88
|
+
"user_input": "pid.to_i",
|
|
89
|
+
"confidence": "Medium",
|
|
90
|
+
"cwe_id": [
|
|
91
|
+
77
|
|
92
|
+
],
|
|
93
|
+
"note": ""
|
|
71
94
|
}
|
|
72
95
|
],
|
|
73
|
-
"updated": "2024-
|
|
74
|
-
"brakeman_version": "6.1
|
|
96
|
+
"updated": "2024-11-16 17:00:20 -0600",
|
|
97
|
+
"brakeman_version": "6.2.1"
|
|
75
98
|
}
|
data/config/locales/de.yml
CHANGED
|
@@ -35,6 +35,20 @@ de:
|
|
|
35
35
|
callback_jobs: Callback-Jobs
|
|
36
36
|
table:
|
|
37
37
|
no_batches_found: Keine Batches gefunden.
|
|
38
|
+
cleaner:
|
|
39
|
+
index:
|
|
40
|
+
all: All
|
|
41
|
+
class: Class
|
|
42
|
+
exception: Exception
|
|
43
|
+
grouped_by_class: Discards grouped by Class
|
|
44
|
+
grouped_by_exception: Discards grouped by Exception
|
|
45
|
+
last_1_hour: Last 1 hour
|
|
46
|
+
last_24_hours: Last 24 hours
|
|
47
|
+
last_3_days: Last 3 days
|
|
48
|
+
last_3_hours: Last 3 hours
|
|
49
|
+
last_7_days: Last 7 days
|
|
50
|
+
title: Discard Cleaner
|
|
51
|
+
total: Total
|
|
38
52
|
cron_entries:
|
|
39
53
|
actions:
|
|
40
54
|
confirm_disable: Bist du sicher, dass du diesen Cron-Eintrag deaktivieren willst?
|
|
@@ -236,6 +250,7 @@ de:
|
|
|
236
250
|
search: Suchen
|
|
237
251
|
navbar:
|
|
238
252
|
batches: Batches
|
|
253
|
+
cleaner: Discard Cleaner
|
|
239
254
|
cron_schedules: Cron
|
|
240
255
|
jobs: Jobs
|
|
241
256
|
live_poll: Live Poll
|
data/config/locales/en.yml
CHANGED
|
@@ -35,6 +35,20 @@ en:
|
|
|
35
35
|
callback_jobs: Callback Jobs
|
|
36
36
|
table:
|
|
37
37
|
no_batches_found: No batches found.
|
|
38
|
+
cleaner:
|
|
39
|
+
index:
|
|
40
|
+
all: All
|
|
41
|
+
class: Class
|
|
42
|
+
exception: Exception
|
|
43
|
+
grouped_by_class: Discards grouped by Class
|
|
44
|
+
grouped_by_exception: Discards grouped by Exception
|
|
45
|
+
last_1_hour: Last 1 hour
|
|
46
|
+
last_24_hours: Last 24 hours
|
|
47
|
+
last_3_days: Last 3 days
|
|
48
|
+
last_3_hours: Last 3 hours
|
|
49
|
+
last_7_days: Last 7 days
|
|
50
|
+
title: Discard Cleaner
|
|
51
|
+
total: Total
|
|
38
52
|
cron_entries:
|
|
39
53
|
actions:
|
|
40
54
|
confirm_disable: Are you sure you want to disable this cron entry?
|
|
@@ -236,6 +250,7 @@ en:
|
|
|
236
250
|
search: Search
|
|
237
251
|
navbar:
|
|
238
252
|
batches: Batches
|
|
253
|
+
cleaner: Discard Cleaner
|
|
239
254
|
cron_schedules: Cron
|
|
240
255
|
jobs: Jobs
|
|
241
256
|
live_poll: Live Poll
|
data/config/locales/es.yml
CHANGED
|
@@ -35,6 +35,20 @@ es:
|
|
|
35
35
|
callback_jobs: Callback Jobs
|
|
36
36
|
table:
|
|
37
37
|
no_batches_found: No hay lotes.
|
|
38
|
+
cleaner:
|
|
39
|
+
index:
|
|
40
|
+
all: All
|
|
41
|
+
class: Class
|
|
42
|
+
exception: Exception
|
|
43
|
+
grouped_by_class: Discards grouped by Class
|
|
44
|
+
grouped_by_exception: Discards grouped by Exception
|
|
45
|
+
last_1_hour: Last 1 hour
|
|
46
|
+
last_24_hours: Last 24 hours
|
|
47
|
+
last_3_days: Last 3 days
|
|
48
|
+
last_3_hours: Last 3 hours
|
|
49
|
+
last_7_days: Last 7 days
|
|
50
|
+
title: Discard Cleaner
|
|
51
|
+
total: Total
|
|
38
52
|
cron_entries:
|
|
39
53
|
actions:
|
|
40
54
|
confirm_disable: "¿Estás seguro que querés deshabilitar esta tarea programada?"
|
|
@@ -236,6 +250,7 @@ es:
|
|
|
236
250
|
search: Buscar
|
|
237
251
|
navbar:
|
|
238
252
|
batches: Lotes
|
|
253
|
+
cleaner: Discard Cleaner
|
|
239
254
|
cron_schedules: Cron
|
|
240
255
|
jobs: Tareas
|
|
241
256
|
live_poll: En vivo
|
data/config/locales/fr.yml
CHANGED
|
@@ -35,6 +35,20 @@ fr:
|
|
|
35
35
|
callback_jobs: Jobs de rappel
|
|
36
36
|
table:
|
|
37
37
|
no_batches_found: Aucun lot trouvé.
|
|
38
|
+
cleaner:
|
|
39
|
+
index:
|
|
40
|
+
all: All
|
|
41
|
+
class: Class
|
|
42
|
+
exception: Exception
|
|
43
|
+
grouped_by_class: Discards grouped by Class
|
|
44
|
+
grouped_by_exception: Discards grouped by Exception
|
|
45
|
+
last_1_hour: Last 1 hour
|
|
46
|
+
last_24_hours: Last 24 hours
|
|
47
|
+
last_3_days: Last 3 days
|
|
48
|
+
last_3_hours: Last 3 hours
|
|
49
|
+
last_7_days: Last 7 days
|
|
50
|
+
title: Discard Cleaner
|
|
51
|
+
total: Total
|
|
38
52
|
cron_entries:
|
|
39
53
|
actions:
|
|
40
54
|
confirm_disable: Voulez-vous vraiment désactiver cette entrée cron ?
|
|
@@ -236,6 +250,7 @@ fr:
|
|
|
236
250
|
search: Rechercher
|
|
237
251
|
navbar:
|
|
238
252
|
batches: Lots
|
|
253
|
+
cleaner: Discard Cleaner
|
|
239
254
|
cron_schedules: Cron
|
|
240
255
|
jobs: Jobs
|
|
241
256
|
live_poll: En direct
|
data/config/locales/it.yml
CHANGED
|
@@ -35,6 +35,20 @@ it:
|
|
|
35
35
|
callback_jobs: Job di callback
|
|
36
36
|
table:
|
|
37
37
|
no_batches_found: Nessun batch trovato.
|
|
38
|
+
cleaner:
|
|
39
|
+
index:
|
|
40
|
+
all: All
|
|
41
|
+
class: Class
|
|
42
|
+
exception: Exception
|
|
43
|
+
grouped_by_class: Discards grouped by Class
|
|
44
|
+
grouped_by_exception: Discards grouped by Exception
|
|
45
|
+
last_1_hour: Last 1 hour
|
|
46
|
+
last_24_hours: Last 24 hours
|
|
47
|
+
last_3_days: Last 3 days
|
|
48
|
+
last_3_hours: Last 3 hours
|
|
49
|
+
last_7_days: Last 7 days
|
|
50
|
+
title: Discard Cleaner
|
|
51
|
+
total: Total
|
|
38
52
|
cron_entries:
|
|
39
53
|
actions:
|
|
40
54
|
confirm_disable: Sei sicuro di voler disabilitare questa voce cron?
|
|
@@ -236,6 +250,7 @@ it:
|
|
|
236
250
|
search: Cerca
|
|
237
251
|
navbar:
|
|
238
252
|
batches: Batch
|
|
253
|
+
cleaner: Discard Cleaner
|
|
239
254
|
cron_schedules: Cron
|
|
240
255
|
jobs: Job
|
|
241
256
|
live_poll: Live Poll
|
data/config/locales/ja.yml
CHANGED
|
@@ -35,6 +35,20 @@ ja:
|
|
|
35
35
|
callback_jobs: コールバックジョブ
|
|
36
36
|
table:
|
|
37
37
|
no_batches_found: バッチが見つかりませんでした。
|
|
38
|
+
cleaner:
|
|
39
|
+
index:
|
|
40
|
+
all: All
|
|
41
|
+
class: Class
|
|
42
|
+
exception: Exception
|
|
43
|
+
grouped_by_class: Discards grouped by Class
|
|
44
|
+
grouped_by_exception: Discards grouped by Exception
|
|
45
|
+
last_1_hour: Last 1 hour
|
|
46
|
+
last_24_hours: Last 24 hours
|
|
47
|
+
last_3_days: Last 3 days
|
|
48
|
+
last_3_hours: Last 3 hours
|
|
49
|
+
last_7_days: Last 7 days
|
|
50
|
+
title: Discard Cleaner
|
|
51
|
+
total: Total
|
|
38
52
|
cron_entries:
|
|
39
53
|
actions:
|
|
40
54
|
confirm_disable: このcronエントリを無効化してもよろしいですか?
|
|
@@ -236,6 +250,7 @@ ja:
|
|
|
236
250
|
search: 検索
|
|
237
251
|
navbar:
|
|
238
252
|
batches: バッチ
|
|
253
|
+
cleaner: Discard Cleaner
|
|
239
254
|
cron_schedules: Cron
|
|
240
255
|
jobs: ジョブ
|
|
241
256
|
live_poll: リアルタイム更新
|
data/config/locales/ko.yml
CHANGED
|
@@ -35,6 +35,20 @@ ko:
|
|
|
35
35
|
callback_jobs: 콜백 작업
|
|
36
36
|
table:
|
|
37
37
|
no_batches_found: 배치를 찾을 수 없습니다.
|
|
38
|
+
cleaner:
|
|
39
|
+
index:
|
|
40
|
+
all: All
|
|
41
|
+
class: Class
|
|
42
|
+
exception: Exception
|
|
43
|
+
grouped_by_class: Discards grouped by Class
|
|
44
|
+
grouped_by_exception: Discards grouped by Exception
|
|
45
|
+
last_1_hour: Last 1 hour
|
|
46
|
+
last_24_hours: Last 24 hours
|
|
47
|
+
last_3_days: Last 3 days
|
|
48
|
+
last_3_hours: Last 3 hours
|
|
49
|
+
last_7_days: Last 7 days
|
|
50
|
+
title: Discard Cleaner
|
|
51
|
+
total: Total
|
|
38
52
|
cron_entries:
|
|
39
53
|
actions:
|
|
40
54
|
confirm_disable: 이 cron 엔트리를 비활성화하시겠습니까?
|
|
@@ -236,6 +250,7 @@ ko:
|
|
|
236
250
|
search: 검색
|
|
237
251
|
navbar:
|
|
238
252
|
batches: 배치
|
|
253
|
+
cleaner: Discard Cleaner
|
|
239
254
|
cron_schedules: Cron
|
|
240
255
|
jobs: 작업
|
|
241
256
|
live_poll: 실시간 업데이트
|
data/config/locales/nl.yml
CHANGED
|
@@ -35,6 +35,20 @@ nl:
|
|
|
35
35
|
callback_jobs: Terugbelopdrachten
|
|
36
36
|
table:
|
|
37
37
|
no_batches_found: Geen batches gevonden.
|
|
38
|
+
cleaner:
|
|
39
|
+
index:
|
|
40
|
+
all: All
|
|
41
|
+
class: Class
|
|
42
|
+
exception: Exception
|
|
43
|
+
grouped_by_class: Discards grouped by Class
|
|
44
|
+
grouped_by_exception: Discards grouped by Exception
|
|
45
|
+
last_1_hour: Last 1 hour
|
|
46
|
+
last_24_hours: Last 24 hours
|
|
47
|
+
last_3_days: Last 3 days
|
|
48
|
+
last_3_hours: Last 3 hours
|
|
49
|
+
last_7_days: Last 7 days
|
|
50
|
+
title: Discard Cleaner
|
|
51
|
+
total: Total
|
|
38
52
|
cron_entries:
|
|
39
53
|
actions:
|
|
40
54
|
confirm_disable: Weet u zekerf dat u deze cron-vermelding wilt uitschakelen?
|
|
@@ -236,6 +250,7 @@ nl:
|
|
|
236
250
|
search: Zoekopdracht
|
|
237
251
|
navbar:
|
|
238
252
|
batches: Batches
|
|
253
|
+
cleaner: Discard Cleaner
|
|
239
254
|
cron_schedules: Cron
|
|
240
255
|
jobs: Taken
|
|
241
256
|
live_poll: Live Poll
|
data/config/locales/pt-BR.yml
CHANGED
|
@@ -35,6 +35,20 @@ pt-BR:
|
|
|
35
35
|
callback_jobs: Tarefas de Callback
|
|
36
36
|
table:
|
|
37
37
|
no_batches_found: Nenhum lote encontrado.
|
|
38
|
+
cleaner:
|
|
39
|
+
index:
|
|
40
|
+
all: All
|
|
41
|
+
class: Class
|
|
42
|
+
exception: Exception
|
|
43
|
+
grouped_by_class: Discards grouped by Class
|
|
44
|
+
grouped_by_exception: Discards grouped by Exception
|
|
45
|
+
last_1_hour: Last 1 hour
|
|
46
|
+
last_24_hours: Last 24 hours
|
|
47
|
+
last_3_days: Last 3 days
|
|
48
|
+
last_3_hours: Last 3 hours
|
|
49
|
+
last_7_days: Last 7 days
|
|
50
|
+
title: Discard Cleaner
|
|
51
|
+
total: Total
|
|
38
52
|
cron_entries:
|
|
39
53
|
actions:
|
|
40
54
|
confirm_disable: Tem certeza de que deseja desativar esta tarefa programada?
|
|
@@ -236,6 +250,7 @@ pt-BR:
|
|
|
236
250
|
search: Pesquisar
|
|
237
251
|
navbar:
|
|
238
252
|
batches: Lotes
|
|
253
|
+
cleaner: Discard Cleaner
|
|
239
254
|
cron_schedules: Agendamentos
|
|
240
255
|
jobs: Tarefas
|
|
241
256
|
live_poll: Acompanhamento ao Vivo
|
data/config/locales/ru.yml
CHANGED
|
@@ -35,6 +35,20 @@ ru:
|
|
|
35
35
|
callback_jobs: Коллбеки
|
|
36
36
|
table:
|
|
37
37
|
no_batches_found: Группы заданий не найдены.
|
|
38
|
+
cleaner:
|
|
39
|
+
index:
|
|
40
|
+
all: All
|
|
41
|
+
class: Class
|
|
42
|
+
exception: Exception
|
|
43
|
+
grouped_by_class: Discards grouped by Class
|
|
44
|
+
grouped_by_exception: Discards grouped by Exception
|
|
45
|
+
last_1_hour: Last 1 hour
|
|
46
|
+
last_24_hours: Last 24 hours
|
|
47
|
+
last_3_days: Last 3 days
|
|
48
|
+
last_3_hours: Last 3 hours
|
|
49
|
+
last_7_days: Last 7 days
|
|
50
|
+
title: Discard Cleaner
|
|
51
|
+
total: Total
|
|
38
52
|
cron_entries:
|
|
39
53
|
actions:
|
|
40
54
|
confirm_disable: Вы уверены, что хотите отключить эту задачу cron?
|
|
@@ -262,6 +276,7 @@ ru:
|
|
|
262
276
|
search: Поиск
|
|
263
277
|
navbar:
|
|
264
278
|
batches: Пакетные задания
|
|
279
|
+
cleaner: Discard Cleaner
|
|
265
280
|
cron_schedules: Cron
|
|
266
281
|
jobs: Задания
|
|
267
282
|
live_poll: Обновления в реальном времени
|
data/config/locales/tr.yml
CHANGED
|
@@ -35,6 +35,20 @@ tr:
|
|
|
35
35
|
callback_jobs: Geri Çağırma İşleri
|
|
36
36
|
table:
|
|
37
37
|
no_batches_found: Toplu İşlem bulunamadı.
|
|
38
|
+
cleaner:
|
|
39
|
+
index:
|
|
40
|
+
all: All
|
|
41
|
+
class: Class
|
|
42
|
+
exception: Exception
|
|
43
|
+
grouped_by_class: Discards grouped by Class
|
|
44
|
+
grouped_by_exception: Discards grouped by Exception
|
|
45
|
+
last_1_hour: Last 1 hour
|
|
46
|
+
last_24_hours: Last 24 hours
|
|
47
|
+
last_3_days: Last 3 days
|
|
48
|
+
last_3_hours: Last 3 hours
|
|
49
|
+
last_7_days: Last 7 days
|
|
50
|
+
title: Discard Cleaner
|
|
51
|
+
total: Total
|
|
38
52
|
cron_entries:
|
|
39
53
|
actions:
|
|
40
54
|
confirm_disable: Bu cron girişini devre dışı bırakmak istediğinizden emin misiniz?
|
|
@@ -236,6 +250,7 @@ tr:
|
|
|
236
250
|
search: Ara
|
|
237
251
|
navbar:
|
|
238
252
|
batches: Toplu İşler
|
|
253
|
+
cleaner: Discard Cleaner
|
|
239
254
|
cron_schedules: Cron
|
|
240
255
|
jobs: İşler
|
|
241
256
|
live_poll: Anlık Güncelleme
|
data/config/locales/uk.yml
CHANGED
|
@@ -35,6 +35,20 @@ uk:
|
|
|
35
35
|
callback_jobs: Задачі з зворотнім викликом
|
|
36
36
|
table:
|
|
37
37
|
no_batches_found: Пакети не знайдені.
|
|
38
|
+
cleaner:
|
|
39
|
+
index:
|
|
40
|
+
all: All
|
|
41
|
+
class: Class
|
|
42
|
+
exception: Exception
|
|
43
|
+
grouped_by_class: Discards grouped by Class
|
|
44
|
+
grouped_by_exception: Discards grouped by Exception
|
|
45
|
+
last_1_hour: Last 1 hour
|
|
46
|
+
last_24_hours: Last 24 hours
|
|
47
|
+
last_3_days: Last 3 days
|
|
48
|
+
last_3_hours: Last 3 hours
|
|
49
|
+
last_7_days: Last 7 days
|
|
50
|
+
title: Discard Cleaner
|
|
51
|
+
total: Total
|
|
38
52
|
cron_entries:
|
|
39
53
|
actions:
|
|
40
54
|
confirm_disable: Ви впевнені, що хочете вимкнути цю cron-запис?
|
|
@@ -262,6 +276,7 @@ uk:
|
|
|
262
276
|
search: Пошук
|
|
263
277
|
navbar:
|
|
264
278
|
batches: Пакети
|
|
279
|
+
cleaner: Discard Cleaner
|
|
265
280
|
cron_schedules: Cron
|
|
266
281
|
jobs: Задачі
|
|
267
282
|
live_poll: Живе Опитування
|
data/config/routes.rb
CHANGED
|
@@ -34,8 +34,8 @@ GoodJob::Engine.routes.draw do
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
resources :processes, only: %i[index]
|
|
37
|
-
|
|
38
37
|
resources :performance, only: %i[index show]
|
|
38
|
+
resources :cleaner, only: %i[index]
|
|
39
39
|
|
|
40
40
|
scope :frontend, controller: :frontends, defaults: { version: GoodJob::VERSION.tr(".", "-") } do
|
|
41
41
|
get "modules/:version/:id", action: :module, as: :frontend_module, constraints: { format: 'js' }
|
|
@@ -32,15 +32,7 @@ module GoodJob # :nodoc:
|
|
|
32
32
|
@record = nil
|
|
33
33
|
@refresh_task = nil
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
# Fall back to PID checking if ForkTracker is not available
|
|
37
|
-
if defined?(ActiveSupport::ForkTracker)
|
|
38
|
-
ActiveSupport::ForkTracker.after_fork { reset }
|
|
39
|
-
@forktracker = true
|
|
40
|
-
else
|
|
41
|
-
@ruby_pid = ::Process.pid
|
|
42
|
-
@forktracker = false
|
|
43
|
-
end
|
|
35
|
+
ActiveSupport::ForkTracker.after_fork { reset }
|
|
44
36
|
|
|
45
37
|
self.class.instances << self
|
|
46
38
|
end
|
|
@@ -53,7 +45,6 @@ module GoodJob # :nodoc:
|
|
|
53
45
|
synchronize do
|
|
54
46
|
next if @locks.zero?
|
|
55
47
|
|
|
56
|
-
reset_on_fork
|
|
57
48
|
if @record
|
|
58
49
|
@record.refresh_if_stale
|
|
59
50
|
else
|
|
@@ -153,7 +144,7 @@ module GoodJob # :nodoc:
|
|
|
153
144
|
# Tests whether an active advisory lock has been taken on the record.
|
|
154
145
|
# @return [Boolean]
|
|
155
146
|
def advisory_locked?
|
|
156
|
-
@advisory_locked_connection&.weakref_alive? && @advisory_locked_connection
|
|
147
|
+
@advisory_locked_connection&.weakref_alive? && @advisory_locked_connection.active?
|
|
157
148
|
end
|
|
158
149
|
|
|
159
150
|
# @!visibility private
|
|
@@ -204,14 +195,6 @@ module GoodJob # :nodoc:
|
|
|
204
195
|
synchronize { ns_reset }
|
|
205
196
|
end
|
|
206
197
|
|
|
207
|
-
def reset_on_fork
|
|
208
|
-
return if Concurrent.on_jruby?
|
|
209
|
-
return if @forktracker || ::Process.pid == @ruby_pid
|
|
210
|
-
|
|
211
|
-
@ruby_pid = ::Process.pid
|
|
212
|
-
ns_reset
|
|
213
|
-
end
|
|
214
|
-
|
|
215
198
|
def ns_reset
|
|
216
199
|
@record_id = SecureRandom.uuid
|
|
217
200
|
@record = nil
|
|
@@ -26,11 +26,13 @@ module GoodJob # :nodoc:
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
# Execution configuration to be scheduled
|
|
29
|
-
# @return [
|
|
29
|
+
# @return [Array<CronEntry>]
|
|
30
30
|
attr_reader :cron_entries
|
|
31
31
|
|
|
32
32
|
# @param cron_entries [Array<CronEntry>]
|
|
33
33
|
# @param start_on_initialize [Boolean]
|
|
34
|
+
# @param graceful_restart_period [ActiveSupport::Duration, nil]
|
|
35
|
+
# @param executor [Concurrent::Executor]
|
|
34
36
|
def initialize(cron_entries = [], start_on_initialize: false, graceful_restart_period: nil, executor: Concurrent.global_io_executor)
|
|
35
37
|
@executor = executor
|
|
36
38
|
@running = false
|
|
@@ -82,16 +84,26 @@ module GoodJob # :nodoc:
|
|
|
82
84
|
|
|
83
85
|
# Enqueues a scheduled task
|
|
84
86
|
# @param cron_entry [CronEntry] the CronEntry object to schedule
|
|
85
|
-
# @param
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
87
|
+
# @param at [Time, nil] When a task needs to optionally be rescheduled because of clock-drift or other inaccuracy
|
|
88
|
+
# @param previously_at [Time, nil] the last +in-memory+ scheduled time the cron task was intended to run
|
|
89
|
+
def create_task(cron_entry, at: nil, previously_at: nil)
|
|
90
|
+
cron_at = at || cron_entry.next_at(previously_at: previously_at)
|
|
91
|
+
|
|
92
|
+
# ScheduledTask runs immediately if delay is <= 0.01; avoid ever scheduling the task before the intended time
|
|
93
|
+
# https://github.com/ruby-concurrency/concurrent-ruby/blob/56227a4c3ebdd53b8b0976eb8296ceb7a093496f/lib/concurrent-ruby/concurrent/executor/timer_set.rb#L97
|
|
94
|
+
delay = cron_at <= Time.current ? 0.0 : [(cron_at - Time.current).to_f, 0.02].max
|
|
95
|
+
|
|
96
|
+
future = Concurrent::ScheduledTask.new(delay, args: [self, cron_entry, cron_at, previously_at], executor: @executor) do |thr_manager, thr_cron_entry, thr_cron_at|
|
|
97
|
+
if thr_cron_at && thr_cron_at > Time.current
|
|
98
|
+
# If clock drift or other inaccuracy, reschedule the task again
|
|
99
|
+
thr_manager.create_task(thr_cron_entry, at: thr_cron_at, previously_at: previously_at)
|
|
100
|
+
else
|
|
101
|
+
# Re-schedule the next cron task before executing the current task
|
|
102
|
+
thr_manager.create_task(thr_cron_entry, previously_at: thr_cron_at)
|
|
103
|
+
|
|
104
|
+
Rails.application.executor.wrap do
|
|
105
|
+
cron_entry.enqueue(thr_cron_at) if thr_cron_entry.enabled?
|
|
106
|
+
end
|
|
95
107
|
end
|
|
96
108
|
end
|
|
97
109
|
|
|
@@ -108,7 +120,7 @@ module GoodJob # :nodoc:
|
|
|
108
120
|
|
|
109
121
|
time_period = @graceful_restart_period.ago..Time.current
|
|
110
122
|
cron_entry.within(time_period).each do |cron_at|
|
|
111
|
-
future = Concurrent::Future.new(args: [self, cron_entry, cron_at], executor: @executor) do |
|
|
123
|
+
future = Concurrent::Future.new(args: [self, cron_entry, cron_at], executor: @executor) do |_thr_manager, thr_cron_entry, thr_cron_at|
|
|
112
124
|
Rails.application.executor.wrap do
|
|
113
125
|
cron_entry.enqueue(thr_cron_at) if thr_cron_entry.enabled?
|
|
114
126
|
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: 4.
|
|
4
|
+
version: 4.5.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-
|
|
11
|
+
date: 2024-11-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activejob
|
|
@@ -254,6 +254,7 @@ files:
|
|
|
254
254
|
- app/charts/good_job/scheduled_by_queue_chart.rb
|
|
255
255
|
- app/controllers/good_job/application_controller.rb
|
|
256
256
|
- app/controllers/good_job/batches_controller.rb
|
|
257
|
+
- app/controllers/good_job/cleaner_controller.rb
|
|
257
258
|
- app/controllers/good_job/cron_entries_controller.rb
|
|
258
259
|
- app/controllers/good_job/frontends_controller.rb
|
|
259
260
|
- app/controllers/good_job/jobs_controller.rb
|
|
@@ -269,6 +270,7 @@ files:
|
|
|
269
270
|
- app/frontend/good_job/modules/charts.js
|
|
270
271
|
- app/frontend/good_job/modules/checkbox_toggle.js
|
|
271
272
|
- app/frontend/good_job/modules/document_ready.js
|
|
273
|
+
- app/frontend/good_job/modules/html_legend_plugin.js
|
|
272
274
|
- app/frontend/good_job/modules/live_poll.js
|
|
273
275
|
- app/frontend/good_job/modules/popovers.js
|
|
274
276
|
- app/frontend/good_job/modules/theme_controller.js
|
|
@@ -304,6 +306,7 @@ files:
|
|
|
304
306
|
- app/views/good_job/batches/_table.erb
|
|
305
307
|
- app/views/good_job/batches/index.html.erb
|
|
306
308
|
- app/views/good_job/batches/show.html.erb
|
|
309
|
+
- app/views/good_job/cleaner/index.html.erb
|
|
307
310
|
- app/views/good_job/cron_entries/index.html.erb
|
|
308
311
|
- app/views/good_job/cron_entries/show.html.erb
|
|
309
312
|
- app/views/good_job/jobs/_executions.erb
|
|
@@ -315,6 +318,7 @@ files:
|
|
|
315
318
|
- app/views/good_job/processes/index.html.erb
|
|
316
319
|
- app/views/good_job/shared/_alert.erb
|
|
317
320
|
- app/views/good_job/shared/_chart.erb
|
|
321
|
+
- app/views/good_job/shared/_chart_container.erb
|
|
318
322
|
- app/views/good_job/shared/_filter.erb
|
|
319
323
|
- app/views/good_job/shared/_footer.erb
|
|
320
324
|
- app/views/good_job/shared/_navbar.erb
|