mission_control-jobs 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +19 -1
- data/app/assets/config/mission_control_jobs_manifest.js +4 -0
- data/app/assets/stylesheets/mission_control/jobs/application.css +16 -2
- data/app/assets/stylesheets/mission_control/jobs/jobs.css +31 -2
- data/app/controllers/mission_control/jobs/application_controller.rb +0 -14
- data/app/controllers/mission_control/jobs/discards_controller.rb +6 -1
- data/app/controllers/mission_control/jobs/jobs_controller.rb +1 -0
- data/app/helpers/mission_control/jobs/dates_helper.rb +2 -16
- data/app/helpers/mission_control/jobs/jobs_helper.rb +12 -3
- data/app/helpers/mission_control/jobs/navigation_helper.rb +13 -1
- data/app/models/mission_control/jobs/recurring_task.rb +1 -1
- data/app/views/layouts/mission_control/jobs/application.html.erb +2 -2
- data/app/views/mission_control/jobs/jobs/_error_information.html.erb +5 -1
- data/app/views/mission_control/jobs/jobs/_filters.html.erb +4 -4
- data/app/views/mission_control/jobs/jobs/_general_information.html.erb +3 -3
- data/app/views/mission_control/jobs/jobs/_job.html.erb +1 -1
- data/app/views/mission_control/jobs/jobs/_jobs_page.html.erb +1 -1
- data/app/views/mission_control/jobs/jobs/_title.html.erb +3 -0
- data/app/views/mission_control/jobs/jobs/blocked/_job.html.erb +1 -1
- data/app/views/mission_control/jobs/jobs/failed/_backtrace_toggle.html.erb +14 -0
- data/app/views/mission_control/jobs/jobs/failed/_job.html.erb +1 -1
- data/app/views/mission_control/jobs/jobs/finished/_job.html.erb +1 -1
- data/app/views/mission_control/jobs/jobs/in_progress/_job.html.erb +1 -1
- data/app/views/mission_control/jobs/jobs/scheduled/_job.html.erb +1 -1
- data/app/views/mission_control/jobs/queues/_job.html.erb +1 -1
- data/app/views/mission_control/jobs/queues/index.html.erb +2 -2
- data/app/views/mission_control/jobs/queues/show.html.erb +1 -1
- data/app/views/mission_control/jobs/recurring_tasks/_recurring_task.html.erb +2 -1
- data/app/views/mission_control/jobs/recurring_tasks/index.html.erb +1 -0
- data/app/views/mission_control/jobs/shared/_job.html.erb +3 -3
- data/app/views/mission_control/jobs/shared/_jobs.html.erb +2 -2
- data/app/views/mission_control/jobs/workers/_worker.html.erb +1 -1
- data/app/views/mission_control/jobs/workers/_workers_page.html.erb +1 -1
- data/config/importmap.rb +1 -1
- data/lib/active_job/queue_adapters/async_ext.rb +49 -0
- data/lib/active_job/queue_adapters/resque_ext.rb +2 -2
- data/lib/active_job/queue_adapters/solid_queue_ext/recurring_tasks.rb +1 -0
- data/lib/mission_control/jobs/application.rb +4 -1
- data/lib/mission_control/jobs/engine.rb +9 -16
- data/lib/mission_control/jobs/server.rb +3 -2
- data/lib/mission_control/jobs/version.rb +1 -1
- data/lib/mission_control/jobs.rb +2 -0
- metadata +71 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4f46c22e66d7fc57d1543dd2fa8863b8b7284019ea4eb47b879a06702c0d89b9
|
4
|
+
data.tar.gz: b891c7389226107a06ce9c5e53b824b91bf3be55e1d3ffe49a2e6419887f5553
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bfc58d081a8dacd1f1c680284a342d6d078f60992dafb0bc2ba5b90cf80e17fe86a240d2fdc5d987adb15231474542f2fe4a067db77884ce2fe523e598ba6b2a
|
7
|
+
data.tar.gz: c8dcc5e285e09c4a1b9607633c82565329496fe9d8adab7288a0d39895ac0dc0cb7ffe90ed31d30320c172224659c7a67441bd70c4e4d957c4d643862e955367
|
data/README.md
CHANGED
@@ -57,6 +57,7 @@ Besides `base_controller_class`, you can also set the following for `MissionCont
|
|
57
57
|
- `internal_query_count_limit`: in count queries, the maximum number of records that will be counted if the adapter needs to limit these queries. True counts above this number will be returned as `INFINITY`. This keeps count queries fast—defaults to `500,000`
|
58
58
|
- `scheduled_job_delay_threshold`: the time duration before a scheduled job is considered delayed. Defaults to `1.minute` (a job is considered delayed if it hasn't transitioned from the `scheduled` status 1 minute after the scheduled time).
|
59
59
|
- `show_console_help`: whether to show the console help. If you don't want the console help message, set this to `false`—defaults to `true`.
|
60
|
+
- `backtrace_cleaner`: a backtrace cleaner used for optionally filtering backtraces on the Failed Jobs detail page. Defaults to `Rails::BacktraceCleaner.new`. See the [Advanced configuration](#advanced-configuration) section for how to configure/override this setting on a per application/server basis.
|
60
61
|
|
61
62
|
This library extends Active Job with a querying interface and the following setting:
|
62
63
|
- `config.active_job.default_page_size`: the internal batch size that Active Job will use when sending queries to the underlying adapter and the batch size for the bulk operations defined above—defaults to `1000`.
|
@@ -107,7 +108,24 @@ SERVERS_BY_APP.each do |app, servers|
|
|
107
108
|
ActiveJob::QueueAdapters::SolidQueueAdapter.new
|
108
109
|
end
|
109
110
|
|
110
|
-
|
111
|
+
# Default:
|
112
|
+
#
|
113
|
+
# @return Array<String, ActiveJob::QueueAdapters::Base)
|
114
|
+
# An array where:
|
115
|
+
# * the String represents the symbolic name for this server within the UI
|
116
|
+
# * ActiveJob::QueueAdapters::Base adapter instance used to access this Application Server/Service
|
117
|
+
[ server, queue_adapter ]
|
118
|
+
|
119
|
+
# Optional return formats:
|
120
|
+
#
|
121
|
+
# @return Array<String, Array<ActiveJob::QueueAdapters::Base>>
|
122
|
+
# * This is equivalent, and behaves identically to, the format the default format above.
|
123
|
+
# [ server, [ queue_adapter ]] # without optional backtrace cleaner
|
124
|
+
#
|
125
|
+
# @return Array<String, Array<ActiveJob::QueueAdapters::Base, ActiveSupport::BacktraceCleaner>>
|
126
|
+
# * This format adds an optional ActiveSupport::BacktraceCleaner to override the system wide
|
127
|
+
# backtrace cleaner for *this* Application Server/Service.
|
128
|
+
# [ server, [ queue_adapter, BacktraceCleaner.new ]] # with optional backtrace cleaner
|
111
129
|
end.to_h
|
112
130
|
|
113
131
|
MissionControl::Jobs.applications.add(app, queue_adapters_by_name)
|
@@ -1,2 +1,16 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
16
|
+
|
@@ -2,6 +2,35 @@
|
|
2
2
|
width: 15rem;
|
3
3
|
}
|
4
4
|
|
5
|
-
.jobs
|
6
|
-
|
5
|
+
table.jobs {
|
6
|
+
td {
|
7
|
+
word-break: break-all;
|
8
|
+
}
|
9
|
+
th.job-header{
|
10
|
+
width: 30%;
|
11
|
+
}
|
12
|
+
th.duration-header {
|
13
|
+
width: 20%;
|
14
|
+
}
|
15
|
+
&.queues th.job-header {
|
16
|
+
width: 30%;
|
17
|
+
}
|
18
|
+
&.failed th.job-header {
|
19
|
+
width: 35%;
|
20
|
+
}
|
21
|
+
&.in_progress th.job-header {
|
22
|
+
width: 50%;
|
23
|
+
}
|
24
|
+
&.blocked th.job-header {
|
25
|
+
width: 45%;
|
26
|
+
}
|
27
|
+
&.scheduled th.job-header {
|
28
|
+
width: 60%;
|
29
|
+
}
|
30
|
+
&.finished th.job-header {
|
31
|
+
width: 65%;
|
32
|
+
}
|
33
|
+
&.workers th.job-header {
|
34
|
+
width: 40%;
|
35
|
+
}
|
7
36
|
}
|
@@ -1,25 +1,11 @@
|
|
1
1
|
class MissionControl::Jobs::ApplicationController < MissionControl::Jobs.base_controller_class.constantize
|
2
|
-
ActionController::Base::MODULES.each do |mod|
|
3
|
-
include mod unless self < mod
|
4
|
-
end
|
5
|
-
|
6
2
|
layout "mission_control/jobs/application"
|
7
3
|
|
8
|
-
# Include helpers if not already included
|
9
|
-
helper MissionControl::Jobs::ApplicationHelper unless self < MissionControl::Jobs::ApplicationHelper
|
10
|
-
helper Importmap::ImportmapTagsHelper unless self < Importmap::ImportmapTagsHelper
|
11
|
-
|
12
4
|
include MissionControl::Jobs::ApplicationScoped, MissionControl::Jobs::NotFoundRedirections
|
13
5
|
include MissionControl::Jobs::AdapterFeatures
|
14
6
|
|
15
|
-
around_action :set_current_locale
|
16
|
-
|
17
7
|
private
|
18
8
|
def default_url_options
|
19
9
|
{ server_id: MissionControl::Jobs::Current.server }
|
20
10
|
end
|
21
|
-
|
22
|
-
def set_current_locale(&block)
|
23
|
-
I18n.with_locale(:en, &block)
|
24
|
-
end
|
25
11
|
end
|
@@ -3,11 +3,16 @@ class MissionControl::Jobs::DiscardsController < MissionControl::Jobs::Applicati
|
|
3
3
|
|
4
4
|
def create
|
5
5
|
@job.discard
|
6
|
-
redirect_to
|
6
|
+
redirect_to redirect_location, notice: "Discarded job with id #{@job.job_id}"
|
7
7
|
end
|
8
8
|
|
9
9
|
private
|
10
10
|
def jobs_relation
|
11
11
|
ActiveJob.jobs.failed
|
12
12
|
end
|
13
|
+
|
14
|
+
def redirect_location
|
15
|
+
status = @job.status.presence_in(supported_job_statuses) || :failed
|
16
|
+
application_jobs_url(@application, status)
|
17
|
+
end
|
13
18
|
end
|
@@ -1,19 +1,5 @@
|
|
1
1
|
module MissionControl::Jobs::DatesHelper
|
2
|
-
def
|
3
|
-
|
4
|
-
end
|
5
|
-
|
6
|
-
def time_distance_in_words_with_title(time)
|
7
|
-
tag.span distance_of_time_in_words_to_now(time, include_seconds: true), title: "Since #{time.to_fs(:long)}"
|
8
|
-
end
|
9
|
-
|
10
|
-
def bidirectional_time_distance_in_words_with_title(time)
|
11
|
-
time_distance = if time.past?
|
12
|
-
"#{distance_of_time_in_words_to_now(time, include_seconds: true)} ago"
|
13
|
-
else
|
14
|
-
"in #{distance_of_time_in_words_to_now(time, include_seconds: true)}"
|
15
|
-
end
|
16
|
-
|
17
|
-
tag.span time_distance, title: time.to_fs(:long)
|
2
|
+
def formatted_time(time)
|
3
|
+
time.in_time_zone.strftime("%Y-%m-%d %H:%M:%S.%3N %Z")
|
18
4
|
end
|
19
5
|
end
|
@@ -11,8 +11,16 @@ module MissionControl::Jobs::JobsHelper
|
|
11
11
|
"#{job.last_execution_error.error_class}: #{job.last_execution_error.message}"
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
|
14
|
+
def clean_backtrace?
|
15
|
+
params["clean_backtrace"] == "true"
|
16
|
+
end
|
17
|
+
|
18
|
+
def failed_job_backtrace(job, server)
|
19
|
+
if clean_backtrace? && server&.backtrace_cleaner
|
20
|
+
server.backtrace_cleaner.clean(job.last_execution_error.backtrace).join("\n")
|
21
|
+
else
|
22
|
+
job.last_execution_error.backtrace.join("\n")
|
23
|
+
end
|
16
24
|
end
|
17
25
|
|
18
26
|
def attribute_names_for_job_status(status)
|
@@ -21,7 +29,7 @@ module MissionControl::Jobs::JobsHelper
|
|
21
29
|
when "blocked" then [ "Queue", "Blocked by", "Block expiry", "" ]
|
22
30
|
when "finished" then [ "Queue", "Finished" ]
|
23
31
|
when "scheduled" then [ "Queue", "Scheduled", "" ]
|
24
|
-
when "in_progress" then [ "Queue", "Run by", "Running
|
32
|
+
when "in_progress" then [ "Queue", "Run by", "Running since" ]
|
25
33
|
else []
|
26
34
|
end
|
27
35
|
end
|
@@ -31,6 +39,7 @@ module MissionControl::Jobs::JobsHelper
|
|
31
39
|
end
|
32
40
|
|
33
41
|
private
|
42
|
+
|
34
43
|
def renderable_job_arguments_for(job)
|
35
44
|
job.serialized_arguments.collect do |argument|
|
36
45
|
as_renderable_argument(argument)
|
@@ -47,6 +47,18 @@ module MissionControl::Jobs::NavigationHelper
|
|
47
47
|
|
48
48
|
def jobs_count_with_status(status)
|
49
49
|
count = ActiveJob.jobs.with_status(status).count
|
50
|
-
count.infinite?
|
50
|
+
if count.infinite?
|
51
|
+
"..."
|
52
|
+
else
|
53
|
+
number_to_human(count,
|
54
|
+
format: "%n%u",
|
55
|
+
units: {
|
56
|
+
thousand: "K",
|
57
|
+
million: "M",
|
58
|
+
billion: "B",
|
59
|
+
trillion: "T",
|
60
|
+
quadrillion: "Q"
|
61
|
+
})
|
62
|
+
end
|
51
63
|
end
|
52
64
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class MissionControl::Jobs::RecurringTask
|
2
2
|
include ActiveModel::Model
|
3
3
|
|
4
|
-
attr_accessor :id, :job_class_name, :command, :arguments, :schedule, :last_enqueued_at, :queue_name, :priority
|
4
|
+
attr_accessor :id, :job_class_name, :command, :arguments, :schedule, :last_enqueued_at, :next_time, :queue_name, :priority
|
5
5
|
|
6
6
|
def initialize(queue_adapter: ActiveJob::Base.queue_adapter, **kwargs)
|
7
7
|
@queue_adapter = queue_adapter
|
@@ -7,9 +7,9 @@
|
|
7
7
|
|
8
8
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
9
9
|
<meta name="turbo-cache-control" content="no-cache">
|
10
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.
|
10
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.1/css/bulma.min.css">
|
11
11
|
<%= stylesheet_link_tag "mission_control/jobs/application", "data-turbo-track": "reload" %>
|
12
|
-
<%= javascript_importmap_tags "application
|
12
|
+
<%= javascript_importmap_tags "application", importmap: MissionControl::Jobs.importmap %>
|
13
13
|
</head>
|
14
14
|
<body>
|
15
15
|
|
@@ -14,5 +14,9 @@
|
|
14
14
|
</tbody>
|
15
15
|
</table>
|
16
16
|
|
17
|
-
|
17
|
+
<% if @server.backtrace_cleaner %>
|
18
|
+
<%= render "mission_control/jobs/jobs/failed/backtrace_toggle", application: @application, job: job %>
|
19
|
+
<% end %>
|
20
|
+
|
21
|
+
<pre class="is-family-monospace mb-4 backtrace-content"><%= failed_job_backtrace(job, @server) %></pre>
|
18
22
|
<% end %>
|
@@ -14,15 +14,15 @@
|
|
14
14
|
|
15
15
|
<%= hidden_field_tag :server_id, MissionControl::Jobs::Current.server.id %>
|
16
16
|
|
17
|
-
<datalist id="job-classes"
|
17
|
+
<datalist id="job-classes" class="is-hidden">
|
18
18
|
<% job_class_names.each do |job_class_name| %>
|
19
|
-
<option value="<%= job_class_name %>"
|
19
|
+
<option value="<%= job_class_name %>"></option>
|
20
20
|
<% end %>
|
21
21
|
</datalist>
|
22
22
|
|
23
|
-
<datalist id="queue-names"
|
23
|
+
<datalist id="queue-names" class="is-hidden">
|
24
24
|
<% queue_names.each do |queue_name| %>
|
25
|
-
<option value="<%= queue_name %>"
|
25
|
+
<option value="<%= queue_name %>"></option>
|
26
26
|
<% end %>
|
27
27
|
</datalist>
|
28
28
|
<% end %>
|
@@ -23,14 +23,14 @@
|
|
23
23
|
<tr>
|
24
24
|
<th>Enqueued</th>
|
25
25
|
<td>
|
26
|
-
<%=
|
26
|
+
<%= formatted_time(job.enqueued_at.to_datetime) %>
|
27
27
|
</td>
|
28
28
|
</tr>
|
29
29
|
<% if job.failed? %>
|
30
30
|
<tr>
|
31
31
|
<th>Failed</th>
|
32
32
|
<td>
|
33
|
-
<%=
|
33
|
+
<%= formatted_time(job.failed_at) %>
|
34
34
|
</td>
|
35
35
|
</tr>
|
36
36
|
<% end %>
|
@@ -38,7 +38,7 @@
|
|
38
38
|
<tr>
|
39
39
|
<th>Finished at</th>
|
40
40
|
<td>
|
41
|
-
<%=
|
41
|
+
<%= formatted_time(job.finished_at) %>
|
42
42
|
</td>
|
43
43
|
</tr>
|
44
44
|
<% end %>
|
@@ -6,7 +6,7 @@
|
|
6
6
|
<div class="is-family-monospace"><%= job_arguments(job) %></div>
|
7
7
|
<% end %>
|
8
8
|
|
9
|
-
<div class="has-text-grey is-size-7">Enqueued <%=
|
9
|
+
<div class="has-text-grey is-size-7">Enqueued <%= formatted_time(job.enqueued_at.to_datetime) %></div>
|
10
10
|
</td>
|
11
11
|
|
12
12
|
<%= render "mission_control/jobs/jobs/#{jobs_status}/job", job: job %>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<table class="jobs <%= jobs_status %> table queues is-hoverable is-fullwidth">
|
2
2
|
<thead>
|
3
3
|
<tr>
|
4
|
-
<th
|
4
|
+
<th class="job-header">Job</th>
|
5
5
|
<% attribute_names_for_job_status(jobs_status).each do |attribute| %>
|
6
6
|
<th><%= attribute %></th>
|
7
7
|
<% end %>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<td><%= link_to job.queue_name, application_queue_path(@application, job.queue) %></td>
|
2
2
|
<td><div class="is-family-monospace is-size-7"><%= job.blocked_by %></div></td>
|
3
|
-
<td><%=
|
3
|
+
<td><%= formatted_time(job.blocked_until) %></td>
|
4
4
|
<td class="pr-0">
|
5
5
|
<%= render "mission_control/jobs/jobs/blocked/actions", job: job %>
|
6
6
|
</td>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<%# locals: (application:, job:) %>
|
2
|
+
|
3
|
+
<div class="is-flex is-justify-content-flex-end mb-2 mr-2 backtrace-toggle-selector">
|
4
|
+
<div class="tabs is-toggle is-toggle-rounded is-small">
|
5
|
+
<ul>
|
6
|
+
<li class="<%= class_names('backtrace-clean-link', 'is-active' => clean_backtrace?) %>">
|
7
|
+
<%= link_to "Clean", application_job_path(application, job.job_id, clean_backtrace: true) %>
|
8
|
+
</li>
|
9
|
+
<li class="<%= class_names('backtrace-full-link', 'is-active' => !clean_backtrace?) %>">
|
10
|
+
<%= link_to "Full", application_job_path(application, job.job_id, clean_backtrace: false) %>
|
11
|
+
</li>
|
12
|
+
</ul>
|
13
|
+
</div>
|
14
|
+
</div>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<td>
|
2
2
|
<%= link_to failed_job_error(job), application_job_path(@application, job.job_id, anchor: "error") %>
|
3
|
-
<div class="has-text-grey"><%=
|
3
|
+
<div class="has-text-grey"><%= formatted_time(job.failed_at) %></div>
|
4
4
|
</td>
|
5
5
|
<td class="pr-0">
|
6
6
|
<%= render "mission_control/jobs/jobs/failed/actions", job: job %>
|
@@ -1,2 +1,2 @@
|
|
1
1
|
<td><%= link_to job.queue_name, application_queue_path(@application, job.queue) %></td>
|
2
|
-
<td><div class="has-text-grey"><%=
|
2
|
+
<td><div class="has-text-grey"><%= formatted_time(job.finished_at) %></div></td>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<td><%= link_to job.queue_name, application_queue_path(@application, job.queue) %></td>
|
2
2
|
<td>
|
3
|
-
<%=
|
3
|
+
<%= formatted_time(job.scheduled_at) %>
|
4
4
|
<% if job_delayed?(job) %>
|
5
5
|
<div class="is-danger tag ml-4">delayed</div>
|
6
6
|
<% end %>
|
@@ -3,7 +3,7 @@
|
|
3
3
|
<%= link_to application_job_path(@application, job.job_id, filter: { queue_name: job.queue }) do %>
|
4
4
|
<%= job_title(job) %>
|
5
5
|
<% end %>
|
6
|
-
<div class="has-text-grey">Enqueued <%=
|
6
|
+
<div class="has-text-grey">Enqueued on <%= formatted_time(job.enqueued_at.to_datetime) %></div>
|
7
7
|
</td>
|
8
8
|
<td>
|
9
9
|
<% if job.serialized_arguments.present? %>
|
@@ -14,5 +14,6 @@
|
|
14
14
|
<% end %>
|
15
15
|
</td>
|
16
16
|
<td> <%= recurring_task.schedule %> </td>
|
17
|
-
<td><div class="has-text-grey"><%= recurring_task.last_enqueued_at ?
|
17
|
+
<td><div class="has-text-grey"><%= recurring_task.last_enqueued_at ? formatted_time(recurring_task.last_enqueued_at) : "Never" %></div></td>
|
18
|
+
<td class="next_time"><div class="has-text-grey"><%= formatted_time(recurring_task.next_time) %></div></td>
|
18
19
|
</tr>
|
@@ -3,7 +3,7 @@
|
|
3
3
|
<%= link_to application_job_path(@application, job.job_id, filter: { queue_name: job.queue }) do %>
|
4
4
|
<%= job_title(job) %>
|
5
5
|
<% end %>
|
6
|
-
<div class="has-text-grey">Enqueued <%=
|
6
|
+
<div class="has-text-grey">Enqueued on <%= formatted_time(job.enqueued_at.to_datetime) %></div>
|
7
7
|
</td>
|
8
8
|
<td>
|
9
9
|
<% if job.serialized_arguments.present? %>
|
@@ -16,9 +16,9 @@
|
|
16
16
|
<td>
|
17
17
|
<div class="has-text-grey">
|
18
18
|
<% if job.started_at %>
|
19
|
-
Running
|
19
|
+
Running since <%= formatted_time(job.started_at) %>
|
20
20
|
<% elsif job.finished_at %>
|
21
|
-
Finished <%=
|
21
|
+
Finished on <%= formatted_time(job.finished_at) %>
|
22
22
|
<% else %>
|
23
23
|
Pending
|
24
24
|
<% end %>
|
data/config/importmap.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
pin "application
|
1
|
+
pin "application", to: "mission_control/jobs/application.js", preload: true
|
2
2
|
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
|
3
3
|
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
|
4
4
|
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module ActiveJob::QueueAdapters::AsyncExt
|
2
|
+
include MissionControl::Jobs::Adapter
|
3
|
+
|
4
|
+
# List of filters supported natively. Non-supported filters are done in memory.
|
5
|
+
def supported_job_filters(jobs_relation)
|
6
|
+
[]
|
7
|
+
end
|
8
|
+
|
9
|
+
def supports_queue_pausing?
|
10
|
+
false
|
11
|
+
end
|
12
|
+
|
13
|
+
def queues
|
14
|
+
[]
|
15
|
+
end
|
16
|
+
|
17
|
+
def queue_size(*)
|
18
|
+
0
|
19
|
+
end
|
20
|
+
|
21
|
+
def clear_queue(*)
|
22
|
+
end
|
23
|
+
|
24
|
+
def jobs_count(*)
|
25
|
+
0
|
26
|
+
end
|
27
|
+
|
28
|
+
def fetch_jobs(*)
|
29
|
+
[]
|
30
|
+
end
|
31
|
+
|
32
|
+
def retry_all_jobs(*)
|
33
|
+
end
|
34
|
+
|
35
|
+
def retry_job(job, *)
|
36
|
+
end
|
37
|
+
|
38
|
+
def discard_all_jobs(*)
|
39
|
+
end
|
40
|
+
|
41
|
+
def discard_job(*)
|
42
|
+
end
|
43
|
+
|
44
|
+
def dispatch_job(*)
|
45
|
+
end
|
46
|
+
|
47
|
+
def find_job(*)
|
48
|
+
end
|
49
|
+
end
|
@@ -190,7 +190,7 @@ module ActiveJob::QueueAdapters::ResqueExt
|
|
190
190
|
job.last_execution_error = execution_error_from_resque_job(resque_job_hash)
|
191
191
|
job.raw_data = resque_job_hash
|
192
192
|
job.position = jobs_relation.offset_value + index
|
193
|
-
job.failed_at = resque_job_hash["failed_at"]&.to_datetime
|
193
|
+
job.failed_at = resque_job_hash["failed_at"]&.to_datetime&.utc
|
194
194
|
job.status = job.failed_at.present? ? :failed : :pending
|
195
195
|
end
|
196
196
|
end
|
@@ -263,7 +263,7 @@ module ActiveJob::QueueAdapters::ResqueExt
|
|
263
263
|
|
264
264
|
def requeue(job)
|
265
265
|
resque_job = job.raw_data
|
266
|
-
resque_job["retried_at"] = Time.now.strftime("%Y/%m/%d %H:%M:%S")
|
266
|
+
resque_job["retried_at"] = Time.now.utc.strftime("%Y/%m/%d %H:%M:%S")
|
267
267
|
|
268
268
|
redis.lset(queue_redis_key, job.position, Resque.encode(resque_job))
|
269
269
|
Resque::Job.create(resque_job["queue"], resque_job["payload"]["class"], *resque_job["payload"]["args"])
|
@@ -11,7 +11,10 @@ class MissionControl::Jobs::Application
|
|
11
11
|
|
12
12
|
def add_servers(queue_adapters_by_name)
|
13
13
|
queue_adapters_by_name.each do |name, queue_adapter|
|
14
|
-
|
14
|
+
adapter, cleaner = queue_adapter
|
15
|
+
|
16
|
+
servers << MissionControl::Jobs::Server.new(name: name.to_s, queue_adapter: adapter,
|
17
|
+
backtrace_cleaner: cleaner, application: self)
|
15
18
|
end
|
16
19
|
end
|
17
20
|
end
|
@@ -1,23 +1,18 @@
|
|
1
|
-
require "mission_control/jobs/version"
|
2
|
-
require "mission_control/jobs/engine"
|
3
|
-
|
4
1
|
require "importmap-rails"
|
5
2
|
require "turbo-rails"
|
6
3
|
require "stimulus-rails"
|
7
|
-
require "propshaft"
|
8
4
|
|
9
5
|
module MissionControl
|
10
6
|
module Jobs
|
11
7
|
class Engine < ::Rails::Engine
|
12
8
|
isolate_namespace MissionControl::Jobs
|
13
9
|
|
14
|
-
config.middleware.use ActionDispatch::Flash unless config.action_dispatch.flash
|
15
|
-
|
16
10
|
config.mission_control = ActiveSupport::OrderedOptions.new unless config.try(:mission_control)
|
17
11
|
config.mission_control.jobs = ActiveSupport::OrderedOptions.new
|
18
12
|
|
19
13
|
config.before_initialize do
|
20
14
|
config.mission_control.jobs.applications = MissionControl::Jobs::Applications.new
|
15
|
+
config.mission_control.jobs.backtrace_cleaner ||= Rails::BacktraceCleaner.new
|
21
16
|
|
22
17
|
config.mission_control.jobs.each do |key, value|
|
23
18
|
MissionControl::Jobs.public_send("#{key}=", value)
|
@@ -48,7 +43,7 @@ module MissionControl
|
|
48
43
|
ActiveJob::QueueAdapters::SolidQueueAdapter.prepend ActiveJob::QueueAdapters::SolidQueueExt
|
49
44
|
end
|
50
45
|
|
51
|
-
ActiveJob::QueueAdapters::AsyncAdapter.include
|
46
|
+
ActiveJob::QueueAdapters::AsyncAdapter.include ActiveJob::QueueAdapters::AsyncExt
|
52
47
|
end
|
53
48
|
|
54
49
|
config.after_initialize do |app|
|
@@ -79,25 +74,23 @@ module MissionControl
|
|
79
74
|
MissionControl::Jobs.delay_between_bulk_operation_batches = 2
|
80
75
|
MissionControl::Jobs.logger = ActiveSupport::Logger.new(STDOUT)
|
81
76
|
|
82
|
-
if MissionControl::Jobs.applications.one? && (application = MissionControl::Jobs.applications.first) && application.servers.one?
|
83
|
-
MissionControl::Jobs::Current.application = application
|
84
|
-
MissionControl::Jobs::Current.server = application.servers.first
|
85
|
-
end
|
86
|
-
|
87
77
|
if MissionControl::Jobs.show_console_help
|
88
78
|
puts "\n\nType 'jobs_help' to see how to connect to the available job servers to manage jobs\n\n"
|
89
79
|
end
|
90
80
|
end
|
91
81
|
|
92
82
|
initializer "mission_control-jobs.assets" do |app|
|
93
|
-
app.config.assets.paths << root.join("app/assets/stylesheets")
|
94
83
|
app.config.assets.paths << root.join("app/javascript")
|
95
84
|
app.config.assets.precompile += %w[ mission_control_jobs_manifest ]
|
96
85
|
end
|
97
86
|
|
98
|
-
initializer "mission_control-jobs.importmap",
|
99
|
-
|
100
|
-
|
87
|
+
initializer "mission_control-jobs.importmap", after: "importmap" do |app|
|
88
|
+
MissionControl::Jobs.importmap.draw(root.join("config/importmap.rb"))
|
89
|
+
MissionControl::Jobs.importmap.cache_sweeper(watches: root.join("app/javascript"))
|
90
|
+
|
91
|
+
ActiveSupport.on_load(:action_controller_base) do
|
92
|
+
before_action { MissionControl::Jobs.importmap.cache_sweeper.execute_if_updated }
|
93
|
+
end
|
101
94
|
end
|
102
95
|
end
|
103
96
|
end
|
@@ -4,12 +4,13 @@ class MissionControl::Jobs::Server
|
|
4
4
|
include MissionControl::Jobs::IdentifiedByName
|
5
5
|
include Serializable, RecurringTasks, Workers
|
6
6
|
|
7
|
-
attr_reader :name, :queue_adapter, :application
|
7
|
+
attr_reader :name, :queue_adapter, :application, :backtrace_cleaner
|
8
8
|
|
9
|
-
def initialize(name:, queue_adapter:, application:)
|
9
|
+
def initialize(name:, queue_adapter:, application:, backtrace_cleaner: nil)
|
10
10
|
super(name: name)
|
11
11
|
@queue_adapter = queue_adapter
|
12
12
|
@application = application
|
13
|
+
@backtrace_cleaner = backtrace_cleaner || MissionControl::Jobs.backtrace_cleaner
|
13
14
|
end
|
14
15
|
|
15
16
|
def activating(&block)
|
data/lib/mission_control/jobs.rb
CHANGED
@@ -19,5 +19,7 @@ module MissionControl
|
|
19
19
|
mattr_accessor :internal_query_count_limit, default: 500_000 # Hard limit to keep unlimited count queries fast enough
|
20
20
|
mattr_accessor :show_console_help, default: true
|
21
21
|
mattr_accessor :scheduled_job_delay_threshold, default: 1.minute
|
22
|
+
mattr_accessor :importmap, default: Importmap::Map.new
|
23
|
+
mattr_accessor :backtrace_cleaner
|
22
24
|
end
|
23
25
|
end
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mission_control-jobs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jorge Manrubia
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-11-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: activerecord
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -25,33 +25,75 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '7.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: activejob
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '7.1'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '7.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: actionpack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '7.1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '7.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: actioncable
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '7.1'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '7.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: railties
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '7.1'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '7.1'
|
41
83
|
- !ruby/object:Gem::Dependency
|
42
84
|
name: importmap-rails
|
43
85
|
requirement: !ruby/object:Gem::Requirement
|
44
86
|
requirements:
|
45
87
|
- - ">="
|
46
88
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
89
|
+
version: 1.2.1
|
48
90
|
type: :runtime
|
49
91
|
prerelease: false
|
50
92
|
version_requirements: !ruby/object:Gem::Requirement
|
51
93
|
requirements:
|
52
94
|
- - ">="
|
53
95
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
96
|
+
version: 1.2.1
|
55
97
|
- !ruby/object:Gem::Dependency
|
56
98
|
name: turbo-rails
|
57
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -112,16 +154,16 @@ dependencies:
|
|
112
154
|
name: solid_queue
|
113
155
|
requirement: !ruby/object:Gem::Requirement
|
114
156
|
requirements:
|
115
|
-
- - "
|
157
|
+
- - "~>"
|
116
158
|
- !ruby/object:Gem::Version
|
117
|
-
version: '0
|
159
|
+
version: '1.0'
|
118
160
|
type: :development
|
119
161
|
prerelease: false
|
120
162
|
version_requirements: !ruby/object:Gem::Requirement
|
121
163
|
requirements:
|
122
|
-
- - "
|
164
|
+
- - "~>"
|
123
165
|
- !ruby/object:Gem::Version
|
124
|
-
version: '0
|
166
|
+
version: '1.0'
|
125
167
|
- !ruby/object:Gem::Dependency
|
126
168
|
name: selenium-webdriver
|
127
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -248,6 +290,20 @@ dependencies:
|
|
248
290
|
- - ">="
|
249
291
|
- !ruby/object:Gem::Version
|
250
292
|
version: '0'
|
293
|
+
- !ruby/object:Gem::Dependency
|
294
|
+
name: better_html
|
295
|
+
requirement: !ruby/object:Gem::Requirement
|
296
|
+
requirements:
|
297
|
+
- - ">="
|
298
|
+
- !ruby/object:Gem::Version
|
299
|
+
version: '0'
|
300
|
+
type: :development
|
301
|
+
prerelease: false
|
302
|
+
version_requirements: !ruby/object:Gem::Requirement
|
303
|
+
requirements:
|
304
|
+
- - ">="
|
305
|
+
- !ruby/object:Gem::Version
|
306
|
+
version: '0'
|
251
307
|
- !ruby/object:Gem::Dependency
|
252
308
|
name: sprockets-rails
|
253
309
|
requirement: !ruby/object:Gem::Requirement
|
@@ -300,6 +356,7 @@ files:
|
|
300
356
|
- MIT-LICENSE
|
301
357
|
- README.md
|
302
358
|
- Rakefile
|
359
|
+
- app/assets/config/mission_control_jobs_manifest.js
|
303
360
|
- app/assets/stylesheets/mission_control/jobs/application.css
|
304
361
|
- app/assets/stylesheets/mission_control/jobs/forms.css
|
305
362
|
- app/assets/stylesheets/mission_control/jobs/jobs.css
|
@@ -354,6 +411,7 @@ files:
|
|
354
411
|
- app/views/mission_control/jobs/jobs/blocked/_actions.html.erb
|
355
412
|
- app/views/mission_control/jobs/jobs/blocked/_job.html.erb
|
356
413
|
- app/views/mission_control/jobs/jobs/failed/_actions.html.erb
|
414
|
+
- app/views/mission_control/jobs/jobs/failed/_backtrace_toggle.html.erb
|
357
415
|
- app/views/mission_control/jobs/jobs/failed/_job.html.erb
|
358
416
|
- app/views/mission_control/jobs/jobs/finished/_job.html.erb
|
359
417
|
- app/views/mission_control/jobs/jobs/in_progress/_job.html.erb
|
@@ -394,6 +452,7 @@ files:
|
|
394
452
|
- lib/active_job/jobs_relation.rb
|
395
453
|
- lib/active_job/querying.rb
|
396
454
|
- lib/active_job/queue.rb
|
455
|
+
- lib/active_job/queue_adapters/async_ext.rb
|
397
456
|
- lib/active_job/queue_adapters/resque_ext.rb
|
398
457
|
- lib/active_job/queue_adapters/solid_queue_ext.rb
|
399
458
|
- lib/active_job/queue_adapters/solid_queue_ext/recurring_tasks.rb
|