good_job 2.14.4 → 2.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/README.md +8 -7
- data/engine/app/controllers/good_job/jobs_controller.rb +10 -3
- data/engine/app/helpers/good_job/application_helper.rb +43 -13
- data/engine/app/views/good_job/cron_entries/index.html.erb +1 -1
- data/engine/app/views/good_job/jobs/_executions.erb +46 -0
- data/engine/app/views/good_job/jobs/_table.erb +13 -1
- data/engine/app/views/good_job/jobs/show.html.erb +56 -2
- data/engine/app/views/good_job/shared/_filter.erb +1 -1
- data/engine/app/views/good_job/shared/icons/_check.html.erb +4 -3
- data/engine/app/views/good_job/shared/icons/_clock.html.erb +5 -0
- data/engine/app/views/good_job/shared/icons/_dash_circle.html.erb +5 -0
- data/engine/app/views/good_job/shared/icons/_exclamation.html.erb +4 -3
- data/engine/config/locales/en.yml +13 -0
- data/engine/config/locales/es.yml +13 -0
- data/engine/config/locales/nl.yml +13 -0
- data/engine/config/locales/ru.yml +13 -0
- data/engine/config/routes.rb +1 -3
- data/lib/good_job/active_job_job.rb +16 -32
- data/lib/good_job/cli.rb +5 -5
- data/lib/good_job/configuration.rb +10 -0
- data/lib/good_job/execution.rb +61 -2
- data/lib/good_job/job_performer.rb +1 -1
- data/lib/good_job/log_subscriber.rb +2 -2
- data/lib/good_job/version.rb +1 -1
- data/lib/good_job.rb +12 -9
- metadata +5 -4
- data/engine/app/controllers/good_job/executions_controller.rb +0 -10
- data/engine/app/views/good_job/executions/_table.erb +0 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b978264b509be9bd83e1e5e67f291b2889deff86c8316f876ab9def8264a7dd0
|
4
|
+
data.tar.gz: 9b4172f97c1c7406d3c51ec81f926adaca801baa004aed743a8cac757bb2ce86
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2696f821699ea080d2b77bb7704192222e6347d1b2be7fcf7a5e049d7b8979ceea3f5236c165fb27bb17fa5f9d8c7d2e5bb863b9c7298de9efb892facb6bca7
|
7
|
+
data.tar.gz: 0ab7a37d403e1bec9c0638835c903aff4a1168f33143c7c27acd87c07bf95a61c1475c3e61bea0a7590d167144761cc64b7bc81566746c854c2ae9698aa999fe
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v2.15.0](https://github.com/bensheldon/good_job/tree/v2.15.0) (2022-05-18)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v2.14.4...v2.15.0)
|
6
|
+
|
7
|
+
**Implemented enhancements:**
|
8
|
+
|
9
|
+
- Adds the ability to delete jobs on the dashboard; add `cleanup_discarded_jobs` option to retain discarded jobs during cleanup [\#597](https://github.com/bensheldon/good_job/pull/597) ([TAGraves](https://github.com/TAGraves))
|
10
|
+
- Dashboard: show more details about jobs [\#575](https://github.com/bensheldon/good_job/pull/575) ([bkeepers](https://github.com/bkeepers))
|
11
|
+
|
12
|
+
**Closed issues:**
|
13
|
+
|
14
|
+
- Show status on jobs\#show page [\#547](https://github.com/bensheldon/good_job/issues/547)
|
15
|
+
|
16
|
+
**Merged pull requests:**
|
17
|
+
|
18
|
+
- Remove ability to destroy individual Executions from Dashboard; rename "Toggle" to "Inspect" everywhere [\#601](https://github.com/bensheldon/good_job/pull/601) ([bensheldon](https://github.com/bensheldon))
|
19
|
+
- Disable ActiveRecord Connection Reaper in test [\#600](https://github.com/bensheldon/good_job/pull/600) ([bensheldon](https://github.com/bensheldon))
|
20
|
+
- Update README dashboard screenshot [\#599](https://github.com/bensheldon/good_job/pull/599) ([aried3r](https://github.com/aried3r))
|
21
|
+
|
3
22
|
## [v2.14.4](https://github.com/bensheldon/good_job/tree/v2.14.4) (2022-05-15)
|
4
23
|
|
5
24
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v2.14.3...v2.14.4)
|
data/README.md
CHANGED
@@ -185,7 +185,7 @@ separate isolated execution pools with semicolons and threads with colons.
|
|
185
185
|
|
186
186
|
#### `good_job cleanup_preserved_jobs`
|
187
187
|
|
188
|
-
`good_job cleanup_preserved_jobs`
|
188
|
+
`good_job cleanup_preserved_jobs` destroys preserved job records. See `GoodJob.preserve_job_records` for when this command is useful.
|
189
189
|
|
190
190
|
```bash
|
191
191
|
$ bundle exec good_job help cleanup_preserved_jobs
|
@@ -194,11 +194,11 @@ Usage:
|
|
194
194
|
good_job cleanup_preserved_jobs
|
195
195
|
|
196
196
|
Options:
|
197
|
-
[--before-seconds-ago=SECONDS] #
|
197
|
+
[--before-seconds-ago=SECONDS] # Destroy records finished more than this many seconds ago (env var: GOOD_JOB_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO, default: 86400)
|
198
198
|
|
199
|
-
|
199
|
+
Destroys preserved job records.
|
200
200
|
|
201
|
-
By default, GoodJob
|
201
|
+
By default, GoodJob destroys job records when the job is performed and this
|
202
202
|
command is not necessary.
|
203
203
|
|
204
204
|
However, when `GoodJob.preserve_job_records = true`, the jobs will be
|
@@ -206,7 +206,7 @@ preserved in the database. This is useful when wanting to analyze or
|
|
206
206
|
inspect job performance.
|
207
207
|
|
208
208
|
If you are preserving job records this way, use this command regularly
|
209
|
-
to
|
209
|
+
to destroy old records and preserve space in your database.
|
210
210
|
```
|
211
211
|
|
212
212
|
### Configuration options
|
@@ -269,6 +269,7 @@ Available configuration options are:
|
|
269
269
|
- `shutdown_timeout` (float) number of seconds to wait for jobs to finish when shutting down before stopping the thread. Defaults to forever: `-1`. You can also set this with the environment variable `GOOD_JOB_SHUTDOWN_TIMEOUT`.
|
270
270
|
- `enable_cron` (boolean) whether to run cron process. Defaults to `false`. You can also set this with the environment variable `GOOD_JOB_ENABLE_CRON`.
|
271
271
|
- `cron` (hash) cron configuration. Defaults to `{}`. You can also set this as a JSON string with the environment variable `GOOD_JOB_CRON`
|
272
|
+
- `cleanup_discarded_jobs` (boolean) whether to destroy discarded jobs when cleaning up preserved jobs using the `$ good_job cleanup_preserved_jobs` CLI command or calling `GoodJob.cleanup_preserved_jobs`. Defaults to `true`. Can also be set with the environment variable `GOOD_JOB_CLEANUP_DISCARDED_JOBS`. _This configuration is only used when {GoodJob.preserve_job_records} is `true`._
|
272
273
|
- `cleanup_preserved_jobs_before_seconds_ago` (integer) number of seconds to preserve jobs when using the `$ good_job cleanup_preserved_jobs` CLI command or calling `GoodJob.cleanup_preserved_jobs`. Defaults to `86400` (1 day). Can also be set with the environment variable `GOOD_JOB_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO`. _This configuration is only used when {GoodJob.preserve_job_records} is `true`._
|
273
274
|
- `cleanup_interval_jobs` (integer) Number of jobs a Scheduler will execute before cleaning up preserved jobs. Defaults to `nil`. Can also be set with the environment variable `GOOD_JOB_CLEANUP_INTERVAL_JOBS`.
|
274
275
|
- `cleanup_interval_seconds` (integer) Number of seconds a Scheduler will wait before cleaning up preserved jobs. Defaults to `nil`. Can also be set with the environment variable `GOOD_JOB_CLEANUP_INTERVAL_SECONDS`.
|
@@ -826,7 +827,7 @@ If your application is already using an ActiveJob backend, you will need to inst
|
|
826
827
|
|
827
828
|
GoodJob is fully instrumented with [`ActiveSupport::Notifications`](https://edgeguides.rubyonrails.org/active_support_instrumentation.html#introduction-to-instrumentation).
|
828
829
|
|
829
|
-
By default, GoodJob will
|
830
|
+
By default, GoodJob will destroy job records after they are run, regardless of whether they succeed or not (raising a kind of `StandardError`), unless they are interrupted (raising a kind of `Exception`).
|
830
831
|
|
831
832
|
To preserve job records for later inspection, set an initializer:
|
832
833
|
|
@@ -835,7 +836,7 @@ To preserve job records for later inspection, set an initializer:
|
|
835
836
|
GoodJob.preserve_job_records = true
|
836
837
|
```
|
837
838
|
|
838
|
-
It is also necessary to
|
839
|
+
It is also necessary to destroy these preserved jobs from the database after a certain time period:
|
839
840
|
|
840
841
|
- For example, in a Rake task:
|
841
842
|
|
@@ -7,6 +7,7 @@ module GoodJob
|
|
7
7
|
discard: "discarded",
|
8
8
|
reschedule: "rescheduled",
|
9
9
|
retry: "retried",
|
10
|
+
destroy: "destroyed",
|
10
11
|
}.freeze
|
11
12
|
|
12
13
|
rescue_from GoodJob::ActiveJobJob::AdapterNotGoodJobError,
|
@@ -36,6 +37,8 @@ module GoodJob
|
|
36
37
|
job.reschedule_job
|
37
38
|
when :retry
|
38
39
|
job.retry_job
|
40
|
+
when :destroy
|
41
|
+
job.destroy_job
|
39
42
|
end
|
40
43
|
|
41
44
|
job
|
@@ -53,9 +56,7 @@ module GoodJob
|
|
53
56
|
end
|
54
57
|
|
55
58
|
def show
|
56
|
-
@
|
57
|
-
.order(Arel.sql("COALESCE(scheduled_at, created_at) DESC"))
|
58
|
-
redirect_to jobs_path, alert: "Executions for Active Job #{params[:id]} not found" if @executions.empty?
|
59
|
+
@job = ActiveJobJob.find(params[:id])
|
59
60
|
end
|
60
61
|
|
61
62
|
def discard
|
@@ -76,6 +77,12 @@ module GoodJob
|
|
76
77
|
redirect_back(fallback_location: jobs_path, notice: "Job has been retried")
|
77
78
|
end
|
78
79
|
|
80
|
+
def destroy
|
81
|
+
@job = ActiveJobJob.find(params[:id])
|
82
|
+
@job.destroy_job
|
83
|
+
redirect_back(fallback_location: jobs_path, notice: "Job has been destroyed")
|
84
|
+
end
|
85
|
+
|
79
86
|
private
|
80
87
|
|
81
88
|
def redirect_on_error(exception)
|
@@ -1,24 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GoodJob
|
3
3
|
module ApplicationHelper
|
4
|
-
def
|
5
|
-
|
4
|
+
def format_duration(sec)
|
5
|
+
return unless sec
|
6
|
+
|
7
|
+
if sec < 1
|
8
|
+
t 'duration.milliseconds', ms: (sec * 1000).floor
|
9
|
+
elsif sec < 10
|
10
|
+
t 'duration.less_than_10_seconds', sec: sec.floor
|
11
|
+
elsif sec < 60
|
12
|
+
t 'duration.seconds', sec: sec.floor
|
13
|
+
elsif sec < 3600
|
14
|
+
t 'duration.minutes', min: (sec / 60).floor, sec: (sec % 60).floor
|
15
|
+
else
|
16
|
+
t 'duration.hours', hour: (sec / 3600).floor, min: ((sec % 3600) / 60).floor
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def relative_time(timestamp, **args)
|
21
|
+
text = timestamp.future? ? "in #{time_ago_in_words(timestamp, **args)}" : "#{time_ago_in_words(timestamp, **args)} ago"
|
6
22
|
tag.time(text, datetime: timestamp, title: timestamp)
|
7
23
|
end
|
8
24
|
|
25
|
+
STATUS_ICONS = {
|
26
|
+
discarded: "exclamation",
|
27
|
+
finished: "check",
|
28
|
+
queued: "dash_circle",
|
29
|
+
retried: "arrow_clockwise",
|
30
|
+
running: "play",
|
31
|
+
scheduled: "clock",
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
STATUS_COLOR = {
|
35
|
+
discarded: "danger",
|
36
|
+
finished: "success",
|
37
|
+
queued: "warning",
|
38
|
+
retried: "secondary",
|
39
|
+
running: "primary",
|
40
|
+
scheduled: "secondary",
|
41
|
+
}.freeze
|
42
|
+
|
9
43
|
def status_badge(status)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
when :queued, :scheduled, :retried
|
14
|
-
"badge rounded-pill bg-secondary"
|
15
|
-
when :running
|
16
|
-
"badge rounded-pill bg-primary"
|
17
|
-
when :discarded
|
18
|
-
"badge rounded-pill bg-danger"
|
19
|
-
end
|
44
|
+
content_tag :span, status_icon(status, class: "text-white") + t(status, scope: '.status'),
|
45
|
+
class: "badge rounded-pill bg-#{STATUS_COLOR.fetch(status)} d-inline-flex gap-2 ps-1 pe-3 align-items-center"
|
46
|
+
end
|
20
47
|
|
21
|
-
|
48
|
+
def status_icon(status, **options)
|
49
|
+
options[:class] ||= "text-#{STATUS_COLOR.fetch(status)}"
|
50
|
+
icon = render_icon STATUS_ICONS.fetch(status)
|
51
|
+
content_tag :span, icon, **options
|
22
52
|
end
|
23
53
|
|
24
54
|
def render_icon(name)
|
@@ -27,7 +27,7 @@
|
|
27
27
|
<td class="font-monospace"><%= cron_entry.key %></td>
|
28
28
|
<td class="font-monospace"><%= cron_entry.schedule %></td>
|
29
29
|
<td>
|
30
|
-
<%= tag.button("
|
30
|
+
<%= tag.button("Inspect", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
|
31
31
|
data: { bs_toggle: "collapse", bs_target: "##{dom_id(cron_entry, 'properties')}" },
|
32
32
|
aria: { expanded: false, controls: dom_id(cron_entry, 'properties') }) %>
|
33
33
|
<%= tag.pre(JSON.pretty_generate(cron_entry.display_properties), id: dom_id(cron_entry, 'properties'), class: "collapse cron-entry-properties") %>
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<h5>Executions</h5>
|
2
|
+
<div class="card mb-4" data-live-poll-region="executions-table">
|
3
|
+
<div class="list-group list-group-flush">
|
4
|
+
<% executions.each do |execution| %>
|
5
|
+
<%= tag.div id: dom_id(execution), class: "list-group-item py-3" do %>
|
6
|
+
<div class="d-md-flex">
|
7
|
+
<div class="flex-fill">
|
8
|
+
<div class="small text-muted">
|
9
|
+
#<%= execution.number %>:
|
10
|
+
<%= tag.code link_to(execution.id, "##{dom_id(execution)}", class: "text-muted text-decoration-none") %>
|
11
|
+
</div>
|
12
|
+
<div class="d-flex gap-2 align-items-center text-muted mt-1">
|
13
|
+
<%= status_badge execution.status %>
|
14
|
+
<%= relative_time execution.last_status_at, include_seconds: true %>
|
15
|
+
|
16
|
+
<% if execution.runtime_latency %>
|
17
|
+
• <div><%= format_duration execution.runtime_latency %> runtime</div>
|
18
|
+
<% end %>
|
19
|
+
<% if execution.queue_latency %>
|
20
|
+
• <div><%= format_duration execution.queue_latency %> in queue</div>
|
21
|
+
<% end %>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
<div>
|
25
|
+
<div class="mt-4 d-flex gap-2">
|
26
|
+
<%= tag.button type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
|
27
|
+
data: { bs_toggle: "collapse", bs_target: "##{dom_id(execution, 'params')}" },
|
28
|
+
aria: { expanded: false, controls: dom_id(execution, "params") } do %>
|
29
|
+
Inspect
|
30
|
+
<% end %>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
<% if execution.error %>
|
35
|
+
<div class="mt-3">
|
36
|
+
<strong class="small">Error:</strong>
|
37
|
+
<pre class="text-wrap text-break m-0"><%= execution.error %></pre>
|
38
|
+
</div>
|
39
|
+
<% end %>
|
40
|
+
<div>
|
41
|
+
<%= tag.pre JSON.pretty_generate(execution.serialized_params), id: dom_id(execution, "params"), class: "collapse bg-light card card-body p-3 my-3" %>
|
42
|
+
</div>
|
43
|
+
<% end %>
|
44
|
+
<% end %>
|
45
|
+
</div>
|
46
|
+
</div>
|
@@ -34,6 +34,10 @@
|
|
34
34
|
<%= form.button type: 'submit', name: 'mass_action', value: 'retry', class: 'btn btn-sm btn-outline-primary', title: "Retry all", data: { confirm: "Confirm retry all", disable: true } do %>
|
35
35
|
<%= render_icon "arrow_clockwise" %> All
|
36
36
|
<% end %>
|
37
|
+
|
38
|
+
<%= form.button type: 'submit', name: 'mass_action', value: 'destroy', class: 'btn btn-sm btn-outline-primary', title: "Destroy all", data: { confirm: "Confirm destroy all", disable: true } do %>
|
39
|
+
<%= render_icon "trash" %> All
|
40
|
+
<% end %>
|
37
41
|
</div>
|
38
42
|
</th>
|
39
43
|
</tr>
|
@@ -63,7 +67,7 @@
|
|
63
67
|
<td><%= job.executions_count %></td>
|
64
68
|
<td class="text-break"><%= truncate(job.recent_error, length: 1_000) %></td>
|
65
69
|
<td>
|
66
|
-
<%= tag.button "
|
70
|
+
<%= tag.button "Inspect", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
|
67
71
|
data: { bs_toggle: "collapse", bs_target: "##{dom_id(job, 'params')}" },
|
68
72
|
aria: { expanded: false, controls: dom_id(job, "params") }
|
69
73
|
%>
|
@@ -94,6 +98,14 @@
|
|
94
98
|
<% else %>
|
95
99
|
<button class="btn btn-sm btn-outline-secondary" disabled><%= render_icon "arrow_clockwise" %></button>
|
96
100
|
<% end %>
|
101
|
+
|
102
|
+
<% if job.status.in? [:discarded, :finished] %>
|
103
|
+
<%= link_to job_path(job.id), method: :delete, class: "btn btn-sm btn-outline-primary", title: "Destroy job", data: { confirm: "Confirm destroy", disable: true } do %>
|
104
|
+
<%= render_icon "trash" %>
|
105
|
+
<% end %>
|
106
|
+
<% else %>
|
107
|
+
<button class="btn btn-sm btn-outline-secondary" disabled><%= render_icon "trash" %></button>
|
108
|
+
<% end %>
|
97
109
|
</div>
|
98
110
|
</td>
|
99
111
|
</tr>
|
@@ -1,3 +1,57 @@
|
|
1
|
-
<
|
1
|
+
<div class="break-out bg-light border-bottom py-2 mb-3">
|
2
|
+
<div class="container-fluid pt-2">
|
3
|
+
<div class="d-flex align-items-center">
|
4
|
+
<div class="flex-fill">
|
5
|
+
<nav aria-label="breadcrumb">
|
6
|
+
<ol class="breadcrumb small mb-0">
|
7
|
+
<li class="breadcrumb-item"><%= link_to "Jobs", jobs_path %></li>
|
8
|
+
<li class="breadcrumb-item active" aria-current="page">ActiveJob ID: <%= tag.code @job.id %></li>
|
9
|
+
</ol>
|
10
|
+
</nav>
|
11
|
+
<h2 class="mb-1"><%= tag.code @job.job_class %></h2>
|
12
|
+
<div class="text-muted small d-flex gap-2">
|
13
|
+
<div>Queue: <%= tag.strong @job.queue_name %></div>
|
14
|
+
•
|
15
|
+
<div>Priority: <%= tag.strong @job.priority %></div>
|
16
|
+
</div>
|
17
|
+
</div>
|
18
|
+
<div>
|
19
|
+
<% job_reschedulable = @job.status.in? [:scheduled, :retried, :queued] %>
|
20
|
+
<%= button_to reschedule_job_path(@job.id), method: :put,
|
21
|
+
class: "btn btn-sm #{job_reschedulable ? 'btn-outline-primary' : 'btn-outline-secondary'}",
|
22
|
+
form_class: "d-inline-block",
|
23
|
+
disabled: !job_reschedulable,
|
24
|
+
aria: { label: "Reschedule job" },
|
25
|
+
title: "Reschedule job",
|
26
|
+
data: { confirm: "Confirm reschedule" } do %>
|
27
|
+
<%= render_icon "skip_forward" %>
|
28
|
+
Reschedule
|
29
|
+
<% end %>
|
2
30
|
|
3
|
-
|
31
|
+
<% job_discardable = @job.status.in? [:scheduled, :retried, :queued] %>
|
32
|
+
<%= button_to discard_job_path(@job.id), method: :put, class: "btn btn-sm #{job_discardable ? 'btn-outline-primary' : 'btn-outline-secondary'}", form_class: "d-inline-block", disabled: !job_discardable, aria: { label: "Discard job" }, title: "Discard job", data: { confirm: "Confirm discard" } do %>
|
33
|
+
<%= render_icon "stop" %>
|
34
|
+
Discard
|
35
|
+
<% end %>
|
36
|
+
|
37
|
+
<%= button_to retry_job_path(@job.id), method: :put, class: "btn btn-sm #{@job.status == :discarded ? 'btn-outline-primary' : 'btn-outline-secondary'}", form_class: "d-inline-block", disabled: @job.status != :discarded, aria: { label: "Retry job" }, title: "Retry job", data: { confirm: "Confirm retry" } do %>
|
38
|
+
<%= render_icon "arrow_clockwise" %>
|
39
|
+
Retry
|
40
|
+
<% end %>
|
41
|
+
|
42
|
+
<% job_destroyable = @job.status.in? [:discarded, :finished] %>
|
43
|
+
<%= button_to job_path(@job.id), method: :delete, class: "btn btn-sm #{job_destroyable ? 'btn-outline-primary' : 'btn-outline-secondary'}", form_class: "d-inline-block", disabled: !job_destroyable, aria: { label: "Destroy job" }, title: "Destroy job", data: { confirm: "Confirm destroy" } do %>
|
44
|
+
<%= render_icon "trash" %>
|
45
|
+
Destroy
|
46
|
+
<% end %>
|
47
|
+
</div>
|
48
|
+
</div>
|
49
|
+
</div>
|
50
|
+
</div>
|
51
|
+
|
52
|
+
<div class="my-4">
|
53
|
+
<h5>Arguments</h5>
|
54
|
+
<%= tag.pre @job.serialized_params["arguments"].map(&:inspect).join(', ') %>
|
55
|
+
</div>
|
56
|
+
|
57
|
+
<%= render 'executions', executions: @job.executions.reverse %>
|
@@ -10,7 +10,7 @@
|
|
10
10
|
<% filter.states.each do |name, count| %>
|
11
11
|
<li class="nav-item">
|
12
12
|
<%= link_to url_for({state: name}), class: "nav-link #{"active" if params[:state] == name}" do %>
|
13
|
-
<%= name.
|
13
|
+
<%= t(name, scope: '.status') %>
|
14
14
|
<span class="badge bg-primary rounded-pill <%= "bg-secondary" if count == 0 %>"><%= count %></span>
|
15
15
|
<% end %>
|
16
16
|
</li>
|
@@ -1,4 +1,5 @@
|
|
1
|
-
<!-- https://icons.getbootstrap.com/icons/check-circle
|
2
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check-circle
|
3
|
-
<path d="
|
1
|
+
<!-- https://icons.getbootstrap.com/icons/check-circle/ -->
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check-circle" viewBox="0 0 16 16">
|
3
|
+
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
4
|
+
<path d="M10.97 4.97a.235.235 0 0 0-.02.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-1.071-1.05z" />
|
4
5
|
</svg>
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<!-- https://icons.getbootstrap.com/icons/clock/ -->
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clock" viewBox="0 0 16 16">
|
3
|
+
<path d="M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71V3.5z" />
|
4
|
+
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0z" />
|
5
|
+
</svg>
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<!-- https://icons.getbootstrap.com/icons/dash-circle/ -->
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-dash-circle" viewBox="0 0 16 16">
|
3
|
+
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
4
|
+
<path d="M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8z" />
|
5
|
+
</svg>
|
@@ -1,4 +1,5 @@
|
|
1
|
-
<!-- https://icons.getbootstrap.com/icons/exclamation-
|
2
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-exclamation-
|
3
|
-
<path d="M8
|
1
|
+
<!-- https://icons.getbootstrap.com/icons/exclamation-circle/ -->
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-exclamation-circle" viewBox="0 0 16 16">
|
3
|
+
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
4
|
+
<path d="M7.002 11a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 4.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995z" />
|
4
5
|
</svg>
|
@@ -39,6 +39,12 @@ en:
|
|
39
39
|
x_years:
|
40
40
|
one: 1 year
|
41
41
|
other: "%{count} years"
|
42
|
+
duration:
|
43
|
+
hours: "%{hour}h %{min}m"
|
44
|
+
less_than_10_seconds: "%{sec}s"
|
45
|
+
milliseconds: "%{ms}ms"
|
46
|
+
minutes: "%{min}m %{sec}s"
|
47
|
+
seconds: "%{sec}s"
|
42
48
|
good_job:
|
43
49
|
shared:
|
44
50
|
footer:
|
@@ -50,3 +56,10 @@ en:
|
|
50
56
|
live_poll: Live Poll
|
51
57
|
name: "GoodJob 👍"
|
52
58
|
processes: Processes
|
59
|
+
status:
|
60
|
+
discarded: Discarded
|
61
|
+
finished: Finished
|
62
|
+
queued: Queued
|
63
|
+
retried: Retried
|
64
|
+
running: Running
|
65
|
+
scheduled: Scheduled
|
@@ -39,6 +39,12 @@ es:
|
|
39
39
|
x_years:
|
40
40
|
one: 1 año
|
41
41
|
other: "%{count} años"
|
42
|
+
duration:
|
43
|
+
hours: "%{hour}h %{min}m"
|
44
|
+
less_than_10_seconds: "%{sec}s"
|
45
|
+
milliseconds: "%{ms}ms"
|
46
|
+
minutes: "%{min}m %{sec}s"
|
47
|
+
seconds: "%{sec}s"
|
42
48
|
good_job:
|
43
49
|
shared:
|
44
50
|
footer:
|
@@ -50,3 +56,10 @@ es:
|
|
50
56
|
live_poll: En vivo
|
51
57
|
name: "GoodJob 👍"
|
52
58
|
processes: Procesos
|
59
|
+
status:
|
60
|
+
discarded: Descartado
|
61
|
+
finished: Acabado
|
62
|
+
queued: Puesto en cola
|
63
|
+
retried: reintentado
|
64
|
+
running: Corriendo
|
65
|
+
scheduled: Programado
|
@@ -39,6 +39,12 @@ nl:
|
|
39
39
|
x_years:
|
40
40
|
one: 1 jaar
|
41
41
|
other: "%{count} jaren"
|
42
|
+
duration:
|
43
|
+
hours: "%{hour}h %{min}m"
|
44
|
+
less_than_10_seconds: "%{sec}s"
|
45
|
+
milliseconds: "%{ms}ms"
|
46
|
+
minutes: "%{min}m %{sec}s"
|
47
|
+
seconds: "%{sec}s"
|
42
48
|
good_job:
|
43
49
|
shared:
|
44
50
|
footer:
|
@@ -50,3 +56,10 @@ nl:
|
|
50
56
|
live_poll: Live Poll
|
51
57
|
name: "GoodJob 👍"
|
52
58
|
processes: Processen
|
59
|
+
status:
|
60
|
+
discarded: weggegooid
|
61
|
+
finished: Afgewerkt
|
62
|
+
queued: In de wachtrij
|
63
|
+
retried: Opnieuw geprobeerd
|
64
|
+
running: Rennen
|
65
|
+
scheduled: Gepland
|
@@ -63,6 +63,12 @@ ru:
|
|
63
63
|
many: "%{count} лет"
|
64
64
|
one: 1 год
|
65
65
|
other: "%{count} года"
|
66
|
+
duration:
|
67
|
+
hours: "%{hour}h %{min}m"
|
68
|
+
less_than_10_seconds: "%{sec}s"
|
69
|
+
milliseconds: "%{ms}мс"
|
70
|
+
minutes: "%{min}м %{sec}с"
|
71
|
+
seconds: "%{sec}s"
|
66
72
|
good_job:
|
67
73
|
shared:
|
68
74
|
footer:
|
@@ -74,3 +80,10 @@ ru:
|
|
74
80
|
live_poll: Живой Опрос
|
75
81
|
name: "GoodJob 👍"
|
76
82
|
processes: Процессы
|
83
|
+
status:
|
84
|
+
discarded: Отброшено
|
85
|
+
finished: Законченный
|
86
|
+
queued: В очереди
|
87
|
+
retried: Повторная попытка
|
88
|
+
running: Бег
|
89
|
+
scheduled: по расписанию
|
data/engine/config/routes.rb
CHANGED
@@ -2,9 +2,7 @@
|
|
2
2
|
GoodJob::Engine.routes.draw do
|
3
3
|
root to: redirect(path: 'jobs')
|
4
4
|
|
5
|
-
resources :
|
6
|
-
|
7
|
-
resources :jobs, only: %i[index show] do
|
5
|
+
resources :jobs, only: %i[index show destroy] do
|
8
6
|
collection do
|
9
7
|
get :mass_update, to: redirect(path: 'jobs')
|
10
8
|
put :mass_update
|
@@ -54,9 +54,11 @@ module GoodJob
|
|
54
54
|
# Advisory locked and executing
|
55
55
|
scope :running, -> { where(finished_at: nil).joins_advisory_locks.where.not(pg_locks: { locktype: nil }) }
|
56
56
|
# Completed executing successfully
|
57
|
-
scope :finished, -> { where.not(finished_at: nil)
|
57
|
+
scope :finished, -> { not_discarded.where.not(finished_at: nil) }
|
58
58
|
# Errored but will not be retried
|
59
59
|
scope :discarded, -> { where.not(finished_at: nil).where.not(error: nil) }
|
60
|
+
# Not errored
|
61
|
+
scope :not_discarded, -> { where(error: nil) }
|
60
62
|
|
61
63
|
# The job's ActiveJob UUID
|
62
64
|
# @return [String]
|
@@ -71,38 +73,8 @@ module GoodJob
|
|
71
73
|
end
|
72
74
|
|
73
75
|
# The status of the Job, based on the state of its most recent execution.
|
74
|
-
# There are 3 buckets of non-overlapping statuses:
|
75
|
-
# 1. The job will be executed
|
76
|
-
# - queued: The job will execute immediately when an execution thread becomes available.
|
77
|
-
# - scheduled: The job is scheduled to execute in the future.
|
78
|
-
# - retried: The job previously errored on execution and will be re-executed in the future.
|
79
|
-
# 2. The job is being executed
|
80
|
-
# - running: the job is actively being executed by an execution thread
|
81
|
-
# 3. The job will not execute
|
82
|
-
# - finished: The job executed successfully
|
83
|
-
# - discarded: The job previously errored on execution and will not be re-executed in the future.
|
84
|
-
#
|
85
76
|
# @return [Symbol]
|
86
|
-
|
87
|
-
execution = head_execution
|
88
|
-
if execution.finished_at.present?
|
89
|
-
if execution.error.present?
|
90
|
-
:discarded
|
91
|
-
else
|
92
|
-
:finished
|
93
|
-
end
|
94
|
-
elsif (execution.scheduled_at || execution.created_at) > DateTime.current
|
95
|
-
if execution.serialized_params.fetch('executions', 0) > 1
|
96
|
-
:retried
|
97
|
-
else
|
98
|
-
:scheduled
|
99
|
-
end
|
100
|
-
elsif running?
|
101
|
-
:running
|
102
|
-
else
|
103
|
-
:queued
|
104
|
-
end
|
105
|
-
end
|
77
|
+
delegate :status, :last_status_at, to: :head_execution
|
106
78
|
|
107
79
|
# This job's most recent {Execution}
|
108
80
|
# @param reload [Booelan] whether to reload executions
|
@@ -225,6 +197,18 @@ module GoodJob
|
|
225
197
|
end
|
226
198
|
end
|
227
199
|
|
200
|
+
# Destroy all of a discarded or finished job's executions from the database so that it will no longer appear on the dashboard.
|
201
|
+
# @return [void]
|
202
|
+
def destroy_job
|
203
|
+
with_advisory_lock do
|
204
|
+
execution = head_execution(reload: true)
|
205
|
+
|
206
|
+
raise ActionForStateMismatchError if execution.finished_at.blank?
|
207
|
+
|
208
|
+
destroy
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
228
212
|
# Utility method to determine which execution record is used to represent this job
|
229
213
|
# @return [String]
|
230
214
|
def _execution_id
|
data/lib/good_job/cli.rb
CHANGED
@@ -120,11 +120,11 @@ module GoodJob
|
|
120
120
|
default_task :start
|
121
121
|
|
122
122
|
# @!macro thor.desc
|
123
|
-
desc :cleanup_preserved_jobs, "
|
123
|
+
desc :cleanup_preserved_jobs, "Destroys preserved job records."
|
124
124
|
long_desc <<~DESCRIPTION
|
125
|
-
|
125
|
+
Destroys preserved job records.
|
126
126
|
|
127
|
-
By default, GoodJob
|
127
|
+
By default, GoodJob destroys job records when the job is performed and this
|
128
128
|
command is not necessary.
|
129
129
|
|
130
130
|
However, when `GoodJob.preserve_job_records = true`, the jobs will be
|
@@ -132,13 +132,13 @@ module GoodJob
|
|
132
132
|
inspect job performance.
|
133
133
|
|
134
134
|
If you are preserving job records this way, use this command regularly
|
135
|
-
to
|
135
|
+
to destroy old records and preserve space in your database.
|
136
136
|
|
137
137
|
DESCRIPTION
|
138
138
|
method_option :before_seconds_ago,
|
139
139
|
type: :numeric,
|
140
140
|
banner: 'SECONDS',
|
141
|
-
desc: "
|
141
|
+
desc: "Destroy records finished more than this many seconds ago (env var: GOOD_JOB_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO, default: 86400)"
|
142
142
|
|
143
143
|
def cleanup_preserved_jobs
|
144
144
|
set_up_application!
|
@@ -171,6 +171,16 @@ module GoodJob
|
|
171
171
|
cron.map { |cron_key, params| GoodJob::CronEntry.new(params.merge(key: cron_key)) }
|
172
172
|
end
|
173
173
|
|
174
|
+
# Whether to destroy discarded jobs when cleaning up preserved jobs.
|
175
|
+
# This configuration is only used when {GoodJob.preserve_job_records} is +true+.
|
176
|
+
# @return [Boolean]
|
177
|
+
def cleanup_discarded_jobs?
|
178
|
+
return rails_config[:cleanup_discarded_jobs] unless rails_config[:cleanup_discarded_jobs].nil?
|
179
|
+
return ActiveModel::Type::Boolean.new.cast(env['GOOD_JOB_CLEANUP_DISCARDED_JOBS']) unless env['GOOD_JOB_CLEANUP_DISCARDED_JOBS'].nil?
|
180
|
+
|
181
|
+
true
|
182
|
+
end
|
183
|
+
|
174
184
|
# Number of seconds to preserve jobs when using the +good_job cleanup_preserved_jobs+ CLI command.
|
175
185
|
# This configuration is only used when {GoodJob.preserve_job_records} is +true+.
|
176
186
|
# @return [Integer]
|
data/lib/good_job/execution.rb
CHANGED
@@ -88,7 +88,7 @@ module GoodJob
|
|
88
88
|
# @return [ActiveRecord::Relation]
|
89
89
|
scope :priority_ordered, -> { order('priority DESC NULLS LAST') }
|
90
90
|
|
91
|
-
# Order jobs by scheduled
|
91
|
+
# Order jobs by scheduled or created (oldest first).
|
92
92
|
# @!method schedule_ordered
|
93
93
|
# @!scope class
|
94
94
|
# @return [ActiveRecord::Relation]
|
@@ -96,7 +96,7 @@ module GoodJob
|
|
96
96
|
|
97
97
|
# Get Jobs were completed before the given timestamp. If no timestamp is
|
98
98
|
# provided, get all jobs that have been completed. By default, GoodJob
|
99
|
-
#
|
99
|
+
# destroys jobs after they are completed and this will find no jobs.
|
100
100
|
# However, if you have changed {GoodJob.preserve_job_records}, this may
|
101
101
|
# find completed Jobs.
|
102
102
|
# @!method finished(timestamp = nil)
|
@@ -272,6 +272,65 @@ module GoodJob
|
|
272
272
|
end
|
273
273
|
end
|
274
274
|
|
275
|
+
# There are 3 buckets of non-overlapping statuses:
|
276
|
+
# 1. The job will be executed
|
277
|
+
# - queued: The job will execute immediately when an execution thread becomes available.
|
278
|
+
# - scheduled: The job is scheduled to execute in the future.
|
279
|
+
# - retried: The job previously errored on execution and will be re-executed in the future.
|
280
|
+
# 2. The job is being executed
|
281
|
+
# - running: the job is actively being executed by an execution thread
|
282
|
+
# 3. The job will not execute
|
283
|
+
# - finished: The job executed successfully
|
284
|
+
# - discarded: The job previously errored on execution and will not be re-executed in the future.
|
285
|
+
#
|
286
|
+
# @return [Symbol]
|
287
|
+
def status
|
288
|
+
if finished_at.present?
|
289
|
+
if error.present?
|
290
|
+
:discarded
|
291
|
+
else
|
292
|
+
:finished
|
293
|
+
end
|
294
|
+
elsif (scheduled_at || created_at) > DateTime.current
|
295
|
+
if serialized_params.fetch('executions', 0) > 1
|
296
|
+
:retried
|
297
|
+
else
|
298
|
+
:scheduled
|
299
|
+
end
|
300
|
+
elsif running?
|
301
|
+
:running
|
302
|
+
else
|
303
|
+
:queued
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def running?
|
308
|
+
performed_at? && !finished_at?
|
309
|
+
end
|
310
|
+
|
311
|
+
def number
|
312
|
+
serialized_params.fetch('executions', 0) + 1
|
313
|
+
end
|
314
|
+
|
315
|
+
# The last relevant timestamp for this execution
|
316
|
+
def last_status_at
|
317
|
+
finished_at || performed_at || scheduled_at || created_at
|
318
|
+
end
|
319
|
+
|
320
|
+
# Time between when this job was expected to run and when it started running
|
321
|
+
def queue_latency
|
322
|
+
now = Time.zone.now
|
323
|
+
expected_start = scheduled_at || created_at
|
324
|
+
actual_start = performed_at || now
|
325
|
+
|
326
|
+
actual_start - expected_start unless expected_start >= now
|
327
|
+
end
|
328
|
+
|
329
|
+
# Time between when this job started and finished
|
330
|
+
def runtime_latency
|
331
|
+
(finished_at || Time.zone.now) - performed_at if performed_at
|
332
|
+
end
|
333
|
+
|
275
334
|
private
|
276
335
|
|
277
336
|
def active_job_data
|
@@ -140,10 +140,10 @@ module GoodJob
|
|
140
140
|
# @!macro notification_responder
|
141
141
|
def cleanup_preserved_jobs(event)
|
142
142
|
timestamp = event.payload[:timestamp]
|
143
|
-
|
143
|
+
destroyed_records_count = event.payload[:destroyed_records_count]
|
144
144
|
|
145
145
|
info do
|
146
|
-
"GoodJob
|
146
|
+
"GoodJob destroyed #{destroyed_records_count} preserved #{'job'.pluralize(destroyed_records_count)} finished before #{timestamp}."
|
147
147
|
end
|
148
148
|
end
|
149
149
|
|
data/lib/good_job/version.rb
CHANGED
data/lib/good_job.rb
CHANGED
@@ -43,7 +43,7 @@ module GoodJob
|
|
43
43
|
# @!attribute [rw] preserve_job_records
|
44
44
|
# @!scope class
|
45
45
|
# Whether to preserve job records in the database after they have finished (default: +false+).
|
46
|
-
# By default, GoodJob
|
46
|
+
# By default, GoodJob destroys job records after the job is completed successfully.
|
47
47
|
# If you want to preserve jobs for latter inspection, set this to +true+.
|
48
48
|
# If you want to preserve only jobs that finished with error for latter inspection, set this to +:on_unhandled_error+.
|
49
49
|
# If +true+, you will need to clean out jobs using the +good_job cleanup_preserved_jobs+ CLI command or
|
@@ -126,25 +126,28 @@ module GoodJob
|
|
126
126
|
end
|
127
127
|
end
|
128
128
|
|
129
|
-
#
|
130
|
-
# By default, GoodJob
|
129
|
+
# Destroys preserved job records.
|
130
|
+
# By default, GoodJob destroys job records when the job is performed and this
|
131
131
|
# method is not necessary. However, when `GoodJob.preserve_job_records = true`,
|
132
132
|
# the jobs will be preserved in the database. This is useful when wanting to
|
133
133
|
# analyze or inspect job performance.
|
134
134
|
# If you are preserving job records this way, use this method regularly to
|
135
|
-
#
|
136
|
-
# @params older_than [nil,Numeric,ActiveSupport::Duration] Jobs older than this will be
|
137
|
-
# @return [Integer] Number of jobs that were
|
135
|
+
# destroy old records and preserve space in your database.
|
136
|
+
# @params older_than [nil,Numeric,ActiveSupport::Duration] Jobs older than this will be destroyed (default: +86400+).
|
137
|
+
# @return [Integer] Number of jobs that were destroyed.
|
138
138
|
def self.cleanup_preserved_jobs(older_than: nil)
|
139
|
-
|
139
|
+
configuration = GoodJob::Configuration.new({})
|
140
|
+
older_than ||= configuration.cleanup_preserved_jobs_before_seconds_ago
|
140
141
|
timestamp = Time.current - older_than
|
142
|
+
include_discarded = configuration.cleanup_discarded_jobs?
|
141
143
|
|
142
144
|
ActiveSupport::Notifications.instrument("cleanup_preserved_jobs.good_job", { older_than: older_than, timestamp: timestamp }) do |payload|
|
143
145
|
old_jobs = GoodJob::ActiveJobJob.where('finished_at <= ?', timestamp)
|
146
|
+
old_jobs = old_jobs.not_discarded unless include_discarded
|
144
147
|
old_jobs_count = old_jobs.count
|
145
148
|
|
146
|
-
GoodJob::Execution.where(job: old_jobs).
|
147
|
-
payload[:
|
149
|
+
GoodJob::Execution.where(job: old_jobs).destroy_all
|
150
|
+
payload[:destroyed_records_count] = old_jobs_count
|
148
151
|
end
|
149
152
|
end
|
150
153
|
|
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.15.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Sheldon
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-05-
|
11
|
+
date: 2022-05-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|
@@ -377,7 +377,6 @@ files:
|
|
377
377
|
- engine/app/controllers/good_job/application_controller.rb
|
378
378
|
- engine/app/controllers/good_job/assets_controller.rb
|
379
379
|
- engine/app/controllers/good_job/cron_entries_controller.rb
|
380
|
-
- engine/app/controllers/good_job/executions_controller.rb
|
381
380
|
- engine/app/controllers/good_job/jobs_controller.rb
|
382
381
|
- engine/app/controllers/good_job/processes_controller.rb
|
383
382
|
- engine/app/filters/good_job/base_filter.rb
|
@@ -385,7 +384,7 @@ files:
|
|
385
384
|
- engine/app/helpers/good_job/application_helper.rb
|
386
385
|
- engine/app/views/good_job/cron_entries/index.html.erb
|
387
386
|
- engine/app/views/good_job/cron_entries/show.html.erb
|
388
|
-
- engine/app/views/good_job/
|
387
|
+
- engine/app/views/good_job/jobs/_executions.erb
|
389
388
|
- engine/app/views/good_job/jobs/_table.erb
|
390
389
|
- engine/app/views/good_job/jobs/index.html.erb
|
391
390
|
- engine/app/views/good_job/jobs/show.html.erb
|
@@ -397,6 +396,8 @@ files:
|
|
397
396
|
- engine/app/views/good_job/shared/_navbar.erb
|
398
397
|
- engine/app/views/good_job/shared/icons/_arrow_clockwise.html.erb
|
399
398
|
- engine/app/views/good_job/shared/icons/_check.html.erb
|
399
|
+
- engine/app/views/good_job/shared/icons/_clock.html.erb
|
400
|
+
- engine/app/views/good_job/shared/icons/_dash_circle.html.erb
|
400
401
|
- engine/app/views/good_job/shared/icons/_exclamation.html.erb
|
401
402
|
- engine/app/views/good_job/shared/icons/_play.html.erb
|
402
403
|
- engine/app/views/good_job/shared/icons/_skip_forward.html.erb
|
@@ -1,10 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GoodJob
|
3
|
-
class ExecutionsController < GoodJob::ApplicationController
|
4
|
-
def destroy
|
5
|
-
deleted_count = GoodJob::Execution.where(id: params[:id]).delete_all
|
6
|
-
message = deleted_count.positive? ? { notice: "Job execution deleted" } : { alert: "Job execution not deleted" }
|
7
|
-
redirect_back fallback_location: jobs_path, **message
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
@@ -1,62 +0,0 @@
|
|
1
|
-
<div class="my-3" data-live-poll-region="executions-table">
|
2
|
-
<div class="table-responsive">
|
3
|
-
<table class="table table-hover table-sm mb-0" id="executions_index_table">
|
4
|
-
<thead>
|
5
|
-
<tr>
|
6
|
-
<th>ActiveJob ID</th>
|
7
|
-
<th>Execution ID</th>
|
8
|
-
<th>Job Class</th>
|
9
|
-
<th>Queue</th>
|
10
|
-
<th>Scheduled At</th>
|
11
|
-
<th>Error</th>
|
12
|
-
<th>
|
13
|
-
ActiveJob Params
|
14
|
-
<%= tag.button "Toggle", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
|
15
|
-
data: { bs_toggle: "collapse", bs_target: ".job-params" },
|
16
|
-
aria: { expanded: false, controls: executions.map { |execution| "##{dom_id(execution, "params")}" }.join(" ") }
|
17
|
-
%>
|
18
|
-
</th>
|
19
|
-
<th>Actions</th>
|
20
|
-
</tr>
|
21
|
-
</thead>
|
22
|
-
<tbody>
|
23
|
-
<% if executions.present? %>
|
24
|
-
<% executions.each do |execution| %>
|
25
|
-
<tr id="<%= dom_id(execution) %>">
|
26
|
-
<td>
|
27
|
-
<%= link_to job_path(execution.serialized_params['job_id']) do %>
|
28
|
-
<code><%= execution.active_job_id %></code>
|
29
|
-
<% end %>
|
30
|
-
</td>
|
31
|
-
<td>
|
32
|
-
<%= link_to job_path(execution.active_job_id, anchor: dom_id(execution)) do %>
|
33
|
-
<code><%= execution.id %></code>
|
34
|
-
<% end %>
|
35
|
-
</td>
|
36
|
-
<td><%= execution.serialized_params['job_class'] %></td>
|
37
|
-
<td><%= execution.queue_name %></td>
|
38
|
-
<td><%= relative_time(execution.scheduled_at || execution.created_at) %></td>
|
39
|
-
<td class="text-break"><%= truncate(execution.error, length: 1_000) %></td>
|
40
|
-
<td>
|
41
|
-
<%= tag.button "Preview", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
|
42
|
-
data: { bs_toggle: "collapse", bs_target: "##{dom_id(execution, 'params')}" },
|
43
|
-
aria: { expanded: false, controls: dom_id(execution, "params") }
|
44
|
-
%>
|
45
|
-
<%= tag.pre JSON.pretty_generate(execution.serialized_params), id: dom_id(execution, "params"), class: "collapse job-params" %>
|
46
|
-
</td>
|
47
|
-
<td>
|
48
|
-
<%= button_to execution_path(execution.id), method: :delete, class: "btn btn-sm btn-outline-danger", title: "Delete execution", data: { confirm: "Confirm delete" } do %>
|
49
|
-
<%= render_icon "trash" %>
|
50
|
-
<% end %>
|
51
|
-
</td>
|
52
|
-
</tr>
|
53
|
-
<% end %>
|
54
|
-
<% else %>
|
55
|
-
<tr>
|
56
|
-
<td colspan="8" class="py-2 text-center text-muted">No executions found.</td>
|
57
|
-
</tr>
|
58
|
-
<% end %>
|
59
|
-
</tbody>
|
60
|
-
</table>
|
61
|
-
</div>
|
62
|
-
</div>
|