good_job 2.10.0 → 2.11.2
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 +45 -1
- data/README.md +32 -2
- data/engine/app/assets/scripts.js +133 -1
- data/engine/app/assets/style.css +5 -0
- data/engine/app/charts/good_job/scheduled_by_queue_chart.rb +2 -1
- data/engine/app/filters/good_job/jobs_filter.rb +2 -2
- data/engine/app/views/good_job/executions/_table.erb +1 -1
- data/engine/app/views/good_job/executions/index.html.erb +2 -2
- data/engine/app/views/good_job/jobs/_table.erb +1 -1
- data/engine/app/views/good_job/jobs/index.html.erb +2 -2
- data/engine/app/views/good_job/processes/index.html.erb +38 -36
- data/engine/app/views/good_job/shared/_chart.erb +1 -23
- data/engine/app/views/good_job/shared/_filter.erb +2 -1
- data/engine/app/views/layouts/good_job/base.html.erb +21 -1
- data/lib/good_job/active_job_job.rb +8 -1
- data/lib/good_job/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 44ffd48ff0a352758f67aeaaf60eeb7242fdf95c71cf16fcb7089689d72709a6
|
4
|
+
data.tar.gz: da29ba7f5b8f212d989d24fec8c7e5672166d3950bae04f3f29a2ca6e8cb035a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c72947a7302d52b2b4f81a6092fbaca28dc900095fa477e0f7abd6d3419c5825c9693063fcdf6081d00ed93b082be705cef2beb2d1088c90de54737098103df5
|
7
|
+
data.tar.gz: 4d9b6c34665cd02c840841460a2a162592d494fb27bd79e12987d4cda50a93d8959f130f29ccbad7e04b4be79ef5878dd13ae98ff87c6d8c87aad514b8937cd9
|
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,54 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v2.11.2](https://github.com/bensheldon/good_job/tree/v2.11.2) (2022-03-03)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v2.11.1...v2.11.2)
|
6
|
+
|
7
|
+
**Closed issues:**
|
8
|
+
|
9
|
+
- Best practices in deploying and monitoring a queue [\#523](https://github.com/bensheldon/good_job/issues/523)
|
10
|
+
|
11
|
+
**Merged pull requests:**
|
12
|
+
|
13
|
+
- Wrap Rspec before and example blocks with a mutex for JRuby [\#537](https://github.com/bensheldon/good_job/pull/537) ([bensheldon](https://github.com/bensheldon))
|
14
|
+
- Delegate `ActiveJobJob.table_name` to `Execution` and prevent it from being directly assignable [\#536](https://github.com/bensheldon/good_job/pull/536) ([bensheldon](https://github.com/bensheldon))
|
15
|
+
- Enable DB table names customization [\#535](https://github.com/bensheldon/good_job/pull/535) ([dimvic](https://github.com/dimvic))
|
16
|
+
- Added a chapter on how to prepare for production. [\#525](https://github.com/bensheldon/good_job/pull/525) ([stas](https://github.com/stas))
|
17
|
+
|
18
|
+
## [v2.11.1](https://github.com/bensheldon/good_job/tree/v2.11.1) (2022-03-01)
|
19
|
+
|
20
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v2.11.0...v2.11.1)
|
21
|
+
|
22
|
+
**Fixed bugs:**
|
23
|
+
|
24
|
+
- Ensure sticky footer doesn't overlap paginater; fix polling interval to 30 seconds, not ms [\#534](https://github.com/bensheldon/good_job/pull/534) ([bensheldon](https://github.com/bensheldon))
|
25
|
+
|
26
|
+
**Closed issues:**
|
27
|
+
|
28
|
+
- Pagination buttons hidden behind footer [\#533](https://github.com/bensheldon/good_job/issues/533)
|
29
|
+
|
30
|
+
## [v2.11.0](https://github.com/bensheldon/good_job/tree/v2.11.0) (2022-02-27)
|
31
|
+
|
32
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v2.10.0...v2.11.0)
|
33
|
+
|
34
|
+
**Implemented enhancements:**
|
35
|
+
|
36
|
+
- Add support for live polling the dashboard [\#528](https://github.com/bensheldon/good_job/pull/528) ([danielwestendorf](https://github.com/danielwestendorf))
|
37
|
+
|
38
|
+
**Closed issues:**
|
39
|
+
|
40
|
+
- How do I ensure that a the same job can't run twice? \(unique job / avoid duplicates\) [\#531](https://github.com/bensheldon/good_job/issues/531)
|
41
|
+
- Bulk reschedule and discard jobs via dashboard [\#527](https://github.com/bensheldon/good_job/issues/527)
|
42
|
+
- "Live Poll" dashboard [\#526](https://github.com/bensheldon/good_job/issues/526)
|
43
|
+
|
3
44
|
## [v2.10.0](https://github.com/bensheldon/good_job/tree/v2.10.0) (2022-02-18)
|
4
45
|
|
5
46
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v2.9.6...v2.10.0)
|
6
47
|
|
48
|
+
**Implemented enhancements:**
|
49
|
+
|
50
|
+
- Dashboard: update search filters and some small UI updates [\#518](https://github.com/bensheldon/good_job/pull/518) ([multiplegeorges](https://github.com/multiplegeorges))
|
51
|
+
|
7
52
|
**Closed issues:**
|
8
53
|
|
9
54
|
- Cron jobs not getting run [\#519](https://github.com/bensheldon/good_job/issues/519)
|
@@ -15,7 +60,6 @@
|
|
15
60
|
- Fix Benchmark job throughput script [\#522](https://github.com/bensheldon/good_job/pull/522) ([douglara](https://github.com/douglara))
|
16
61
|
- Update development Gemfile.lock [\#521](https://github.com/bensheldon/good_job/pull/521) ([bensheldon](https://github.com/bensheldon))
|
17
62
|
- Ensure Rails 6.0 is tested against Ruby 3.0; use Ruby 3.0 in demo environment [\#520](https://github.com/bensheldon/good_job/pull/520) ([bensheldon](https://github.com/bensheldon))
|
18
|
-
- Dashboard: update search filters and some small UI updates [\#518](https://github.com/bensheldon/good_job/pull/518) ([multiplegeorges](https://github.com/multiplegeorges))
|
19
63
|
- Document safer setting for retry\_on\_unhandled\_error [\#517](https://github.com/bensheldon/good_job/pull/517) ([tamaloa](https://github.com/tamaloa))
|
20
64
|
|
21
65
|
## [v2.9.6](https://github.com/bensheldon/good_job/tree/v2.9.6) (2022-02-07)
|
data/README.md
CHANGED
@@ -52,6 +52,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
|
|
52
52
|
- [Timeouts](#timeouts)
|
53
53
|
- [Optimize queues, threads, and processes](#optimize-queues-threads-and-processes)
|
54
54
|
- [Database connections](#database-connections)
|
55
|
+
- [Production setup](#production-setup)
|
55
56
|
- [Execute jobs async / in-process](#execute-jobs-async--in-process)
|
56
57
|
- [Migrate to GoodJob from a different ActiveJob backend](#migrate-to-goodjob-from-a-different-activejob-backend)
|
57
58
|
- [Monitor and preserve worked jobs](#monitor-and-preserve-worked-jobs)
|
@@ -369,6 +370,10 @@ GoodJob includes a Dashboard as a mountable `Rails::Engine`.
|
|
369
370
|
end
|
370
371
|
```
|
371
372
|
|
373
|
+
#### Live Polling
|
374
|
+
|
375
|
+
The Dashboard can be set to automatically refresh by checking "Live Poll" in the Dashboard header, or by setting `?poll=10` with the interval in seconds (default 30 seconds).
|
376
|
+
|
372
377
|
### ActiveJob concurrency
|
373
378
|
|
374
379
|
GoodJob can extend ActiveJob to provide limits on concurrently running jobs, either at time of _enqueue_ or at _perform_. Limiting concurrency can help prevent duplicate, double or unecessary jobs from being enqueued, or race conditions when performing, for example when interacting with 3rd-party APIs.
|
@@ -670,13 +675,38 @@ Keep in mind, queue operations and management is an advanced discipline. This st
|
|
670
675
|
|
671
676
|
### Database connections
|
672
677
|
|
673
|
-
Each GoodJob execution thread requires its own database connection that is automatically checked out from Rails’ connection pool.
|
678
|
+
Each GoodJob execution thread requires its own database connection that is automatically checked out from Rails’ connection pool. For example:
|
674
679
|
|
675
680
|
```yaml
|
676
681
|
# config/database.yml
|
677
|
-
pool: <%=
|
682
|
+
pool: <%= ENV.fetch("RAILS_MAX_THREADS", 5).to_i + (ENV.fetch("GOOD_JOB_MAX_THREADS", 4).to_i %>
|
678
683
|
```
|
679
684
|
|
685
|
+
To calculate the total number of the database connections you'll need:
|
686
|
+
|
687
|
+
- 1 connection dedicated to the scheduler aka `LISTEN/NOTIFY`
|
688
|
+
- 1 connection per query pool thread e.g. `--queues=mice:2;elephants:1` is 3 threads. Pool thread size defaults to `--max-threads`
|
689
|
+
- (optional) 2 connections for Cron scheduler if you're running it
|
690
|
+
- (optional) 1 connection per subthread, if your application makes multithreaded database queries within a job
|
691
|
+
- When running `:async`, you must also add the number of threads by the webserver
|
692
|
+
|
693
|
+
The queue process will not crash if the connections pool is exhausted, instead it will report an exception (eg. `ActiveRecord::ConnectionTimeoutError`).
|
694
|
+
|
695
|
+
#### Production setup
|
696
|
+
|
697
|
+
When running GoodJob in a production environment, you should be mindful of:
|
698
|
+
|
699
|
+
- [Execution mode](execute-jobs-async--in-process)
|
700
|
+
- [Database connection pool size](#database-connections)
|
701
|
+
- [Health check probes](#cli-http-health-check-probes) and potentially the [instrumentation support](#monitor-and-preserve-worked-jobs)
|
702
|
+
|
703
|
+
The recommended way to monitor the queue in production is:
|
704
|
+
|
705
|
+
- have an exception notifier callback (see `on_thread_error`)
|
706
|
+
- if possible, run the queue as a dedicated instance and use available HTTP health check probes instead of pid-based monitoring
|
707
|
+
- keep an eye on the number of jobs in the queue (abnormal high number of unscheduled jobs means the queue could be underperforming)
|
708
|
+
- consider performance monitoring services which support the built-in Rails instrumentation (eg. Sentry, Skylight, etc.)
|
709
|
+
|
680
710
|
### Execute jobs async / in-process
|
681
711
|
|
682
712
|
GoodJob can execute jobs "async" in the same process as the web server (e.g. `bin/rails s`). GoodJob's async execution mode offers benefits of economy by not requiring a separate job worker process, but with the tradeoff of increased complexity. Async mode can be configured in two ways:
|
@@ -1 +1,133 @@
|
|
1
|
-
|
1
|
+
/*jshint esversion: 6, strict: false */
|
2
|
+
const GOOD_JOB_DEFAULT_POLL_INTERVAL_SECONDS = 30;
|
3
|
+
const GOOD_JOB_MINIMUM_POLL_INTERVAL = 1000;
|
4
|
+
|
5
|
+
const GoodJob = {
|
6
|
+
// Register functions to execute when the DOM is ready
|
7
|
+
ready: (callback) => {
|
8
|
+
if (document.readyState !== "loading") {
|
9
|
+
callback();
|
10
|
+
} else {
|
11
|
+
document.addEventListener("DOMContentLoaded", callback);
|
12
|
+
}
|
13
|
+
},
|
14
|
+
|
15
|
+
init: () => {
|
16
|
+
GoodJob.updateSettings();
|
17
|
+
GoodJob.addListeners();
|
18
|
+
GoodJob.pollUpdates();
|
19
|
+
GoodJob.renderCharts(true);
|
20
|
+
},
|
21
|
+
|
22
|
+
addListeners: () => {
|
23
|
+
const gjActionEls = document.querySelectorAll('[data-gj-action]');
|
24
|
+
|
25
|
+
for (let i = 0; i < gjActionEls.length; i++) {
|
26
|
+
const el = gjActionEls[i];
|
27
|
+
const [eventName, func] = el.dataset.gjAction.split('#');
|
28
|
+
|
29
|
+
el.addEventListener(eventName, GoodJob[func]);
|
30
|
+
}
|
31
|
+
},
|
32
|
+
|
33
|
+
updateSettings: () => {
|
34
|
+
const queryString = window.location.search;
|
35
|
+
const urlParams = new URLSearchParams(queryString);
|
36
|
+
|
37
|
+
// live poll interval and enablement
|
38
|
+
if (urlParams.has('poll')) {
|
39
|
+
const parsedInterval = (parseInt(urlParams.get('poll')) || GOOD_JOB_DEFAULT_POLL_INTERVAL_SECONDS) * 1000;
|
40
|
+
GoodJob.pollInterval = Math.max(parsedInterval, GOOD_JOB_MINIMUM_POLL_INTERVAL);
|
41
|
+
GoodJob.setStorage('pollInterval', GoodJob.pollInterval);
|
42
|
+
|
43
|
+
GoodJob.pollEnabled = true;
|
44
|
+
} else {
|
45
|
+
GoodJob.pollInterval = GoodJob.getStorage('pollInterval') || (GOOD_JOB_DEFAULT_POLL_INTERVAL_SECONDS * 1000);
|
46
|
+
GoodJob.pollEnabled = GoodJob.getStorage('pollEnabled') || false;
|
47
|
+
}
|
48
|
+
|
49
|
+
document.getElementById('toggle-poll').checked = GoodJob.pollEnabled;
|
50
|
+
},
|
51
|
+
|
52
|
+
togglePoll: (ev) => {
|
53
|
+
GoodJob.pollEnabled = ev.currentTarget.checked;
|
54
|
+
GoodJob.setStorage('pollEnabled', GoodJob.pollEnabled);
|
55
|
+
},
|
56
|
+
|
57
|
+
pollUpdates: () => {
|
58
|
+
setTimeout(() => {
|
59
|
+
if (GoodJob.pollEnabled === true) {
|
60
|
+
fetch(window.location.href)
|
61
|
+
.then(resp => resp.text())
|
62
|
+
.then(GoodJob.updateContent)
|
63
|
+
.finally(GoodJob.pollUpdates);
|
64
|
+
} else {
|
65
|
+
GoodJob.pollUpdates();
|
66
|
+
}
|
67
|
+
}, GoodJob.pollInterval);
|
68
|
+
},
|
69
|
+
|
70
|
+
updateContent: (newContent) => {
|
71
|
+
const domParser = new DOMParser();
|
72
|
+
const parsedDOM = domParser.parseFromString(newContent, "text/html");
|
73
|
+
|
74
|
+
const newElements = parsedDOM.querySelectorAll('[data-gj-poll-replace]');
|
75
|
+
|
76
|
+
for (let i = 0; i < newElements.length; i++) {
|
77
|
+
const newEl = newElements[i];
|
78
|
+
const oldEl = document.getElementById(newEl.id);
|
79
|
+
|
80
|
+
if (oldEl) {
|
81
|
+
oldEl.replaceWith(newEl);
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
GoodJob.renderCharts(false);
|
86
|
+
},
|
87
|
+
|
88
|
+
renderCharts: (animate) => {
|
89
|
+
const charts = document.querySelectorAll('.chart');
|
90
|
+
|
91
|
+
for (let i = 0; i < charts.length; i++) {
|
92
|
+
const chartEl = charts[i];
|
93
|
+
const chartData = JSON.parse(chartEl.dataset.json);
|
94
|
+
|
95
|
+
const ctx = chartEl.getContext('2d');
|
96
|
+
const chart = new Chart(ctx, {
|
97
|
+
type: 'line',
|
98
|
+
data: {
|
99
|
+
labels: chartData.labels,
|
100
|
+
datasets: chartData.datasets
|
101
|
+
},
|
102
|
+
options: {
|
103
|
+
animation: animate,
|
104
|
+
responsive: true,
|
105
|
+
maintainAspectRatio: false,
|
106
|
+
scales: {
|
107
|
+
y: {
|
108
|
+
beginAtZero: true
|
109
|
+
}
|
110
|
+
}
|
111
|
+
}
|
112
|
+
});
|
113
|
+
}
|
114
|
+
},
|
115
|
+
|
116
|
+
getStorage: (key) => {
|
117
|
+
const value = localStorage.getItem('good_job-' + key);
|
118
|
+
|
119
|
+
if (value === 'true') {
|
120
|
+
return true;
|
121
|
+
} else if (value === 'false') {
|
122
|
+
return false;
|
123
|
+
} else {
|
124
|
+
return value;
|
125
|
+
}
|
126
|
+
},
|
127
|
+
|
128
|
+
setStorage: (key, value) => {
|
129
|
+
localStorage.setItem('good_job-' + key, value);
|
130
|
+
}
|
131
|
+
};
|
132
|
+
|
133
|
+
GoodJob.ready(GoodJob.init);
|
data/engine/app/assets/style.css
CHANGED
@@ -9,6 +9,7 @@ module GoodJob
|
|
9
9
|
def data
|
10
10
|
end_time = Time.current
|
11
11
|
start_time = end_time - 1.day
|
12
|
+
table_name = GoodJob::ActiveJobJob.table_name
|
12
13
|
|
13
14
|
count_query = Arel.sql(GoodJob::Execution.pg_or_jdbc_query(<<~SQL.squish))
|
14
15
|
SELECT *
|
@@ -23,7 +24,7 @@ module GoodJob
|
|
23
24
|
queue_name,
|
24
25
|
count(*) AS count
|
25
26
|
FROM (
|
26
|
-
#{@filter.filtered_query.except(:select, :order).select('queue_name',
|
27
|
+
#{@filter.filtered_query.except(:select, :order).select('queue_name', "COALESCE(#{table_name}.scheduled_at, #{table_name}.created_at)::timestamp AS scheduled_at").to_sql}
|
27
28
|
) sources
|
28
29
|
GROUP BY date_trunc('hour', scheduled_at), queue_name
|
29
30
|
) sources ON sources.scheduled_at = timestamp
|
@@ -14,7 +14,7 @@ module GoodJob
|
|
14
14
|
|
15
15
|
def filtered_query
|
16
16
|
query = base_query.includes(:executions)
|
17
|
-
.joins_advisory_locks.select(
|
17
|
+
.joins_advisory_locks.select("#{GoodJob::ActiveJobJob.table_name}.*", 'pg_locks.locktype AS locktype')
|
18
18
|
|
19
19
|
query = query.job_class(params[:job_class]) if params[:job_class].present?
|
20
20
|
query = query.where(queue_name: params[:queue_name]) if params[:queue_name].present?
|
@@ -31,7 +31,7 @@ module GoodJob
|
|
31
31
|
when 'scheduled'
|
32
32
|
query = query.scheduled
|
33
33
|
when 'running'
|
34
|
-
query = query.running.select(
|
34
|
+
query = query.running.select("#{GoodJob::ActiveJobJob.table_name}.*", 'pg_locks.locktype')
|
35
35
|
when 'queued'
|
36
36
|
query = query.queued
|
37
37
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<div class="card my-3 p-6">
|
1
|
+
<div class="card my-3 p-6" data-gj-poll-replace id="executions-chart">
|
2
2
|
<%= render 'good_job/shared/chart', chart_data: GoodJob::ScheduledByQueueChart.new(@filter).data %>
|
3
3
|
</div>
|
4
4
|
|
@@ -7,7 +7,7 @@
|
|
7
7
|
<%= render 'good_job/executions/table', executions: @filter.records %>
|
8
8
|
|
9
9
|
<% if @filter.records.present? %>
|
10
|
-
<nav aria-label="Job pagination" class="mt-3">
|
10
|
+
<nav aria-label="Job pagination" class="mt-3" data-gj-poll-replace id="executions-pagination">
|
11
11
|
<ul class="pagination">
|
12
12
|
<li class="page-item">
|
13
13
|
<%= link_to({ after_scheduled_at: (@filter.last.scheduled_at || @filter.last.created_at), after_id: @filter.last.id }, class: "page-link") do %>
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<h2>All Jobs</h2>
|
3
3
|
</div>
|
4
4
|
|
5
|
-
<div class="card my-3 p-6">
|
5
|
+
<div class="card my-3 p-6" data-gj-poll-replace id="jobs-chart">
|
6
6
|
<%= render 'good_job/shared/chart', chart_data: GoodJob::ScheduledByQueueChart.new(@filter).data %>
|
7
7
|
</div>
|
8
8
|
|
@@ -11,7 +11,7 @@
|
|
11
11
|
<%= render 'good_job/jobs/table', jobs: @filter.records %>
|
12
12
|
|
13
13
|
<% if @filter.records.present? %>
|
14
|
-
<nav aria-label="Job pagination" class="mt-3">
|
14
|
+
<nav aria-label="Job pagination" class="mt-3" data-gj-poll-replace id="jobs-pagination">
|
15
15
|
<ul class="pagination">
|
16
16
|
<li class="page-item">
|
17
17
|
<%= link_to(@filter.to_params(after_scheduled_at: (@filter.last.scheduled_at || @filter.last.created_at), after_id: @filter.last.id), class: "page-link") do %>
|
@@ -2,43 +2,45 @@
|
|
2
2
|
<h2>Processes</h2>
|
3
3
|
</div>
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
<div class="card-
|
8
|
-
<
|
9
|
-
<
|
10
|
-
|
5
|
+
<div data-gj-poll-replace id="processes">
|
6
|
+
<% if !GoodJob::Process.migrated? %>
|
7
|
+
<div class="card my-3">
|
8
|
+
<div class="card-body">
|
9
|
+
<p class="card-text">
|
10
|
+
<em>Feature unavailable because of pending database migration.</em>
|
11
|
+
</p>
|
12
|
+
</div>
|
11
13
|
</div>
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
<th>State</th>
|
22
|
-
</tr>
|
23
|
-
</thead>
|
24
|
-
<tbody>
|
25
|
-
<% @processes.each do |process| %>
|
26
|
-
<tr class="<%= dom_class(process) %>" id="<%= dom_id(process) %>">
|
27
|
-
<td><%= process.id %></td>
|
28
|
-
<td><%= relative_time(process.created_at) %></td>
|
29
|
-
<td><%= tag.pre JSON.pretty_generate(process.state) %></td>
|
14
|
+
<% elsif @processes.present? %>
|
15
|
+
<div class="card my-3">
|
16
|
+
<div class="table-responsive">
|
17
|
+
<table class="table card-table table-bordered table-hover table-sm mb-0">
|
18
|
+
<thead>
|
19
|
+
<tr>
|
20
|
+
<th>Process UUID</th>
|
21
|
+
<th>Created At</th></th>
|
22
|
+
<th>State</th>
|
30
23
|
</tr>
|
31
|
-
|
32
|
-
|
33
|
-
|
24
|
+
</thead>
|
25
|
+
<tbody>
|
26
|
+
<% @processes.each do |process| %>
|
27
|
+
<tr class="<%= dom_class(process) %>" id="<%= dom_id(process) %>">
|
28
|
+
<td><%= process.id %></td>
|
29
|
+
<td><%= relative_time(process.created_at) %></td>
|
30
|
+
<td><%= tag.pre JSON.pretty_generate(process.state) %></td>
|
31
|
+
</tr>
|
32
|
+
<% end %>
|
33
|
+
</tbody>
|
34
|
+
</table>
|
35
|
+
</div>
|
34
36
|
</div>
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
</
|
37
|
+
<% else %>
|
38
|
+
<div class="card my-3">
|
39
|
+
<div class="card-body">
|
40
|
+
<p class="card-text">
|
41
|
+
<em>No GoodJob processes found.</em>
|
42
|
+
</p>
|
43
|
+
</div>
|
42
44
|
</div>
|
43
|
-
|
44
|
-
|
45
|
+
<% end %>
|
46
|
+
</div>
|
@@ -1,25 +1,3 @@
|
|
1
1
|
<div class="chart-wrapper">
|
2
|
-
<canvas
|
2
|
+
<canvas class="chart" data-json="<%= chart_data.to_json %>"></canvas>
|
3
3
|
</div>
|
4
|
-
|
5
|
-
<%= javascript_tag nonce: true do %>
|
6
|
-
const chartData = <%== chart_data.to_json %>;
|
7
|
-
|
8
|
-
const ctx = document.getElementById('chart').getContext('2d');
|
9
|
-
const chart = new Chart(ctx, {
|
10
|
-
type: 'line',
|
11
|
-
data: {
|
12
|
-
labels: chartData.labels,
|
13
|
-
datasets: chartData.datasets
|
14
|
-
},
|
15
|
-
options: {
|
16
|
-
responsive: true,
|
17
|
-
maintainAspectRatio: false,
|
18
|
-
scales: {
|
19
|
-
y: {
|
20
|
-
beginAtZero: true
|
21
|
-
}
|
22
|
-
}
|
23
|
-
}
|
24
|
-
});
|
25
|
-
<% end %>
|
@@ -1,4 +1,5 @@
|
|
1
1
|
<%= form_with(url: "", method: :get, local: true, id: "filter_form") do |form| %>
|
2
|
+
<%= hidden_field_tag :poll, value: params[:poll] %>
|
2
3
|
<div class="d-flex flex-row w-100">
|
3
4
|
<div class="me-2">
|
4
5
|
<label for="job_class_filter">Job class</label>
|
@@ -55,4 +56,4 @@
|
|
55
56
|
});
|
56
57
|
})
|
57
58
|
})
|
58
|
-
<% end %>
|
59
|
+
<% end %>
|
@@ -45,7 +45,10 @@
|
|
45
45
|
</div>
|
46
46
|
</li>
|
47
47
|
</ul>
|
48
|
-
<div
|
48
|
+
<div>
|
49
|
+
<input type="checkbox" id="toggle-poll" name="toggle-poll" data-gj-action='change#togglePoll' <%= 'checked' if params[:poll].present? %>>
|
50
|
+
<label for="toggle-poll">Live Poll</label>
|
51
|
+
</div>
|
49
52
|
</div>
|
50
53
|
</div>
|
51
54
|
</nav>
|
@@ -70,7 +73,24 @@
|
|
70
73
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
71
74
|
</div>
|
72
75
|
<% end %>
|
76
|
+
|
73
77
|
<%= yield %>
|
74
78
|
</div>
|
79
|
+
|
80
|
+
<footer class="footer mt-auto py-3 bg-light fixed-bottom" id="footer" data-gj-poll-replace>
|
81
|
+
<div class="container-fluid">
|
82
|
+
<div class="row">
|
83
|
+
<div class="col-6">
|
84
|
+
<span class="text-muted">
|
85
|
+
Last updated: <time id="page-updated-at" datetime="<%= Time.current.utc.iso8601 %>"><%= Time.current %></time>
|
86
|
+
</span>
|
87
|
+
</div>
|
88
|
+
|
89
|
+
<div class="col-6 text-end">
|
90
|
+
Remember, you're doing a Good Job too!
|
91
|
+
</div>
|
92
|
+
</div>
|
93
|
+
</div>
|
94
|
+
</footer>
|
75
95
|
</body>
|
76
96
|
</html>
|
@@ -15,7 +15,14 @@ module GoodJob
|
|
15
15
|
# Attached to a Job's Execution when the Job is discarded.
|
16
16
|
DiscardJobError = Class.new(StandardError)
|
17
17
|
|
18
|
-
|
18
|
+
class << self
|
19
|
+
delegate :table_name, to: Execution
|
20
|
+
|
21
|
+
def table_name=(_value)
|
22
|
+
raise NotImplementedError, 'Assign GoodJob::Execution.table_name directly'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
19
26
|
self.primary_key = 'active_job_id'
|
20
27
|
self.advisory_lockable_column = 'active_job_id'
|
21
28
|
|
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: 2.
|
4
|
+
version: 2.11.2
|
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-03-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|