solid_stack_web 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +5 -14
- data/app/assets/stylesheets/solid_stack_web/_07_dashboard.css +4 -0
- data/app/assets/stylesheets/solid_stack_web/_09_detail.css +23 -0
- data/app/controllers/solid_stack_web/dashboard_controller.rb +1 -0
- data/app/controllers/solid_stack_web/failed_jobs/errors_controller.rb +9 -0
- data/app/controllers/solid_stack_web/failed_jobs_controller.rb +11 -0
- data/app/controllers/solid_stack_web/stats_controller.rb +12 -3
- data/app/helpers/solid_stack_web/application_helper.rb +11 -0
- data/app/models/solid_stack_web/error_frequency_report.rb +34 -0
- data/app/models/solid_stack_web/failed_job_sparkline.rb +23 -0
- data/app/views/solid_stack_web/dashboard/index.html.erb +12 -0
- data/app/views/solid_stack_web/failed_jobs/errors/index.html.erb +48 -0
- data/app/views/solid_stack_web/failed_jobs/index.html.erb +10 -1
- data/app/views/solid_stack_web/stats/index.html.erb +4 -0
- data/config/routes.rb +2 -0
- data/lib/solid_stack_web/version.rb +1 -1
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1717d75a5baabd8728f6f9877b5146849be9322a627cf295bd922690aa284592
|
|
4
|
+
data.tar.gz: ae814ccee9aa89798575f914652ccfbb51137473961d7fd1a22b7a4461449102
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e0d79705beafccb16561d20a28cac815607f2a202bc6d0839449c501f8d4f5bd93bd83ccb4a7c4be0d6b701ecb69b9aca8fa5ee2a3d4462712fc8399c935162a
|
|
7
|
+
data.tar.gz: 2663a57cf04ca3aa26463732638020c63ce76d10046979dbd22baae8f5b529d3092e3597aa9c4a8b9b06b0a43b9a648e0a573349bca1f39d6f944fe4a8313884
|
data/README.md
CHANGED
|
@@ -21,23 +21,13 @@ Run:
|
|
|
21
21
|
bundle install
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
Mount the engine in `config/routes.rb`:
|
|
25
|
-
|
|
26
|
-
```ruby
|
|
27
|
-
mount SolidStackWeb::Engine, at: "/solid_stack"
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
The dashboard will be available at `/solid_stack` (or whatever path you choose).
|
|
31
|
-
|
|
32
|
-
### Install generator
|
|
33
|
-
|
|
34
24
|
Run the install generator to create a documented initializer and wire up the mount point in one step:
|
|
35
25
|
|
|
36
26
|
```bash
|
|
37
27
|
rails generate solid_stack_web:install
|
|
38
28
|
```
|
|
39
29
|
|
|
40
|
-
This creates `config/initializers/solid_stack_web.rb` with every configuration option commented inline, and injects `mount SolidStackWeb::Engine, at: "/solid_stack"` into `config/routes.rb`.
|
|
30
|
+
This creates `config/initializers/solid_stack_web.rb` with every configuration option commented inline, and injects `mount SolidStackWeb::Engine, at: "/solid_stack"` into `config/routes.rb`. The dashboard will then be available at `/solid_stack` (or whatever path you choose).
|
|
41
31
|
|
|
42
32
|
---
|
|
43
33
|
|
|
@@ -77,7 +67,7 @@ This creates `config/initializers/solid_stack_web.rb` with every configuration o
|
|
|
77
67
|
|
|
78
68
|
## General configuration
|
|
79
69
|
|
|
80
|
-
|
|
70
|
+
The install generator creates `config/initializers/solid_stack_web.rb` with all options documented inline. The available options are:
|
|
81
71
|
|
|
82
72
|
```ruby
|
|
83
73
|
SolidStackWeb.configure do |config|
|
|
@@ -152,16 +142,17 @@ The dashboard is designed to be mounted behind your application's existing authe
|
|
|
152
142
|
|
|
153
143
|
### Features
|
|
154
144
|
|
|
155
|
-
- **Overview dashboard** — live counts across all queue statuses; done (1h/24h), healthy/stale process counts, and optionally slow jobs (when `slow_job_threshold` is configured); 12-hour throughput sparkline with per-bar hover tooltips
|
|
145
|
+
- **Overview dashboard** — live counts across all queue statuses; done (1h/24h), healthy/stale process counts, and optionally slow jobs (when `slow_job_threshold` is configured); 12-hour throughput sparkline and a 12-hour failures sparkline (red bars) with per-bar hover tooltips — failure spikes visible before clicking into the failed jobs list
|
|
156
146
|
- **Job browser** — browse jobs by status (ready, scheduled, claimed, blocked) with filtering by job class, queue name, priority, and time period; **Discard All** bulk-discards every job matching the current filters in one request; **CSV export** downloads jobs respecting active filters
|
|
157
147
|
- **Bulk selection** — checkbox-select individual jobs for discard; select-all support
|
|
158
148
|
- **Per-queue browser** — click any queue name or size to drill into its ready jobs with per-row and bulk discard; pause/resume controls on the queue page
|
|
159
149
|
- **Queue depth sparklines** — Queues index shows a 12-hour depth chart per queue; each bar is the ready-job count at an hourly snapshot with an instant hover tooltip
|
|
160
150
|
- **Job detail page** — full arguments (pretty-printed JSON), queue, priority, enqueued time, Active Job ID, concurrency key, scheduled/blocked-until metadata, and a Discard button
|
|
161
151
|
- **Failed jobs** — list with retry / discard / bulk retry / bulk discard; **Failed job detail page** — full error, backtrace, and an inline JSON argument editor; submit to update arguments and retry in one action
|
|
152
|
+
- **Error frequency report** — `GET /failed_jobs/errors` groups all failed jobs by exception class and message prefix with a count and expandable sample backtrace; links through to a filtered list for each error group
|
|
162
153
|
- **Scheduled job management** — "Run Now" and offset buttons (+1h / +24h / +7d) per row update the scheduled time inline via Turbo Stream; "Run All Now (N)" back-dates all matching executions at once
|
|
163
154
|
- **Recurring task list** — enumerates all `SolidQueue::RecurringTask` records with cron schedule, job class or command, queue, next-run and last-run times, and a static/dynamic badge; each row has a "Run Now" button
|
|
164
|
-
- **Performance statistics page** — `GET /stats` aggregates finished jobs by class name with execution count, avg, p50, p95, min, and max duration; click any column header to sort; defaults to p95 descending
|
|
155
|
+
- **Performance statistics page** — `GET /stats` aggregates finished jobs by class name with execution count, avg, p50, p95, p99, std dev, min, and max duration; click any column header to sort; defaults to p95 descending; high std dev flags inconsistent jobs worth investigating
|
|
165
156
|
- **Job history view** — paginated list of all finished jobs with class name, queue, duration, and finished-at time; filterable by queue (click a badge), class substring, and time period; CSV export respects active filters
|
|
166
157
|
- **Auto-refresh** — dashboard, jobs, processes, and history views poll automatically; pauses when the tab is hidden or a checkbox is checked; intervals configurable via `dashboard_refresh_interval` and `default_refresh_interval`
|
|
167
158
|
- **Turbo Stream** job discard — removes the row inline without a full page reload
|
|
@@ -84,6 +84,29 @@
|
|
|
84
84
|
|
|
85
85
|
.sqw-value-truncated { font-size: 12px; margin-top: 0.5rem; }
|
|
86
86
|
|
|
87
|
+
.sqw-error-details > summary {
|
|
88
|
+
cursor: pointer;
|
|
89
|
+
list-style: none;
|
|
90
|
+
display: block;
|
|
91
|
+
max-width: 480px;
|
|
92
|
+
}
|
|
93
|
+
.sqw-error-details > summary::-webkit-details-marker { display: none; }
|
|
94
|
+
.sqw-error-details[open] > summary { margin-bottom: 0.5rem; }
|
|
95
|
+
|
|
96
|
+
.sqw-error-backtrace {
|
|
97
|
+
font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
|
|
98
|
+
font-size: 11px;
|
|
99
|
+
background: var(--bg);
|
|
100
|
+
border: 1px solid var(--border);
|
|
101
|
+
border-radius: var(--radius);
|
|
102
|
+
padding: 0.5rem 0.75rem;
|
|
103
|
+
overflow-x: auto;
|
|
104
|
+
white-space: pre;
|
|
105
|
+
max-height: 200px;
|
|
106
|
+
overflow-y: auto;
|
|
107
|
+
margin-top: 0.25rem;
|
|
108
|
+
}
|
|
109
|
+
|
|
87
110
|
.sqw-link { color: var(--primary); text-decoration: none; }
|
|
88
111
|
.sqw-link:hover { text-decoration: underline; }
|
|
89
112
|
|
|
@@ -4,6 +4,8 @@ module SolidStackWeb
|
|
|
4
4
|
respond_to do |format|
|
|
5
5
|
format.html do
|
|
6
6
|
scope = ::SolidQueue::FailedExecution.includes(:job).order(created_at: :desc)
|
|
7
|
+
@error_class = params[:error_class].presence
|
|
8
|
+
scope = scope.where(id: ids_for_error_class(@error_class)) if @error_class
|
|
7
9
|
@pagy, @executions = pagy(scope)
|
|
8
10
|
end
|
|
9
11
|
format.csv do
|
|
@@ -41,6 +43,15 @@ module SolidStackWeb
|
|
|
41
43
|
|
|
42
44
|
private
|
|
43
45
|
|
|
46
|
+
def ids_for_error_class(ec)
|
|
47
|
+
::SolidQueue::FailedExecution.pluck(:id, :error).filter_map do |id, raw|
|
|
48
|
+
error = raw.is_a?(Hash) ? raw : JSON.parse(raw)
|
|
49
|
+
id if error["exception_class"] == ec
|
|
50
|
+
rescue StandardError
|
|
51
|
+
nil
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
44
55
|
def failed_jobs_csv
|
|
45
56
|
CSV.generate(headers: true) do |csv|
|
|
46
57
|
csv << %w[id class_name queue_name error_class error_message failed_at]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module SolidStackWeb
|
|
2
2
|
class StatsController < ApplicationController
|
|
3
|
-
SORTABLE_COLUMNS = %w[class_name count avg p50 p95 min max].freeze
|
|
3
|
+
SORTABLE_COLUMNS = %w[class_name count avg p50 p95 p99 stddev min max].freeze
|
|
4
4
|
|
|
5
5
|
def index
|
|
6
6
|
@sort = params[:sort].presence_in(SORTABLE_COLUMNS) || "p95"
|
|
@@ -18,14 +18,17 @@ module SolidStackWeb
|
|
|
18
18
|
jobs.group_by(&:class_name).map do |class_name, group|
|
|
19
19
|
durations = group.map { |j| (j.finished_at - j.created_at).to_f }.sort
|
|
20
20
|
count = durations.size
|
|
21
|
+
avg = durations.sum / count
|
|
21
22
|
{
|
|
22
23
|
class_name: class_name,
|
|
23
24
|
count: count,
|
|
24
|
-
avg:
|
|
25
|
+
avg: avg,
|
|
25
26
|
min: durations.first,
|
|
26
27
|
max: durations.last,
|
|
27
28
|
p50: percentile(durations, 50),
|
|
28
|
-
p95: percentile(durations, 95)
|
|
29
|
+
p95: percentile(durations, 95),
|
|
30
|
+
p99: percentile(durations, 99),
|
|
31
|
+
stddev: stddev(durations, avg)
|
|
29
32
|
}
|
|
30
33
|
end
|
|
31
34
|
end
|
|
@@ -35,5 +38,11 @@ module SolidStackWeb
|
|
|
35
38
|
k = (sorted.size - 1) * pct / 100.0
|
|
36
39
|
sorted[k.floor] + (sorted[k.ceil] - sorted[k.floor]) * (k - k.floor)
|
|
37
40
|
end
|
|
41
|
+
|
|
42
|
+
def stddev(durations, avg)
|
|
43
|
+
return 0.0 if durations.size < 2
|
|
44
|
+
variance = durations.sum { |d| (d - avg)**2 } / durations.size
|
|
45
|
+
Math.sqrt(variance)
|
|
46
|
+
end
|
|
38
47
|
end
|
|
39
48
|
end
|
|
@@ -87,6 +87,17 @@ module SolidStackWeb
|
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
+
def failed_job_sparkline_svg(sparkline)
|
|
91
|
+
build_sparkline_svg(sparkline, aria_label: "Failed jobs over the last 12 hours") do |count, i|
|
|
92
|
+
hours_ago = SolidStackWeb::FailedJobSparkline::HOURS - i
|
|
93
|
+
if hours_ago == 1
|
|
94
|
+
"#{count} #{count == 1 ? "failure" : "failures"} in the last hour"
|
|
95
|
+
else
|
|
96
|
+
"#{count} #{count == 1 ? "failure" : "failures"} (#{hours_ago}h–#{hours_ago - 1}h ago)"
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
90
101
|
private
|
|
91
102
|
|
|
92
103
|
def build_sparkline_svg(sparkline, css_class: "sqw-sparkline", aria_label: nil, &tooltip_text)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module SolidStackWeb
|
|
2
|
+
class ErrorFrequencyReport
|
|
3
|
+
Row = Data.define(:exception_class, :message_prefix, :count, :sample_backtrace)
|
|
4
|
+
|
|
5
|
+
MESSAGE_LIMIT = 120
|
|
6
|
+
|
|
7
|
+
def groups
|
|
8
|
+
::SolidQueue::FailedExecution
|
|
9
|
+
.order(created_at: :desc)
|
|
10
|
+
.each_with_object({}) do |execution, acc|
|
|
11
|
+
key = [execution.exception_class.to_s, message_prefix(execution.message)]
|
|
12
|
+
entry = acc[key] ||= { count: 0, sample_backtrace: nil }
|
|
13
|
+
entry[:count] += 1
|
|
14
|
+
entry[:sample_backtrace] ||= execution.backtrace
|
|
15
|
+
end
|
|
16
|
+
.map do |(exception_class, prefix), data|
|
|
17
|
+
Row.new(
|
|
18
|
+
exception_class: exception_class,
|
|
19
|
+
message_prefix: prefix,
|
|
20
|
+
count: data[:count],
|
|
21
|
+
sample_backtrace: data[:sample_backtrace]
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
.sort_by { |row| -row.count }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def message_prefix(message)
|
|
30
|
+
return "" if message.nil?
|
|
31
|
+
message.length > MESSAGE_LIMIT ? "#{message[0, MESSAGE_LIMIT]}…" : message
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module SolidStackWeb
|
|
2
|
+
class FailedJobSparkline
|
|
3
|
+
HOURS = 12
|
|
4
|
+
|
|
5
|
+
def buckets
|
|
6
|
+
@buckets ||= begin
|
|
7
|
+
now = Time.current
|
|
8
|
+
origin = now - HOURS.hours
|
|
9
|
+
times = ::SolidQueue::FailedExecution.where(created_at: origin..now).pluck(:created_at)
|
|
10
|
+
|
|
11
|
+
HOURS.times.map do |i|
|
|
12
|
+
from = origin + i.hours
|
|
13
|
+
to = origin + (i + 1).hours
|
|
14
|
+
times.count { |t| t >= from && t < to }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def max
|
|
20
|
+
buckets.max || 0
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -68,6 +68,18 @@
|
|
|
68
68
|
<span>now</span>
|
|
69
69
|
</div>
|
|
70
70
|
</div>
|
|
71
|
+
<div class="sqw-sparkline-wrap sqw-sparkline-wrap--failures" data-controller="sparkline-tooltip">
|
|
72
|
+
<span class="sqw-sparkline-label">Failures — last 12 hours</span>
|
|
73
|
+
<div class="sqw-sparkline-positioner">
|
|
74
|
+
<%= failed_job_sparkline_svg(@failures) %>
|
|
75
|
+
<div class="sqw-sparkline-tip" data-sparkline-tooltip-target="tip" hidden></div>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="sqw-sparkline-axis">
|
|
78
|
+
<span>12h ago</span>
|
|
79
|
+
<span>6h ago</span>
|
|
80
|
+
<span>now</span>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
71
83
|
</div>
|
|
72
84
|
|
|
73
85
|
<div class="sqw-gem-card sqw-gem-card--cache">
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<div class="sqw-page-header sqw-page-header--split">
|
|
2
|
+
<h1 class="sqw-page-title">Error Summary</h1>
|
|
3
|
+
<div class="sqw-header-actions">
|
|
4
|
+
<%= link_to "← Failed Jobs", failed_jobs_path, class: "sqw-btn sqw-btn--muted sqw-btn--sm" %>
|
|
5
|
+
</div>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<% if @groups.any? %>
|
|
9
|
+
<table class="sqw-table">
|
|
10
|
+
<thead>
|
|
11
|
+
<tr>
|
|
12
|
+
<th scope="col">Error Class</th>
|
|
13
|
+
<th scope="col">Message</th>
|
|
14
|
+
<th scope="col">Count</th>
|
|
15
|
+
<th scope="col"><span class="sqw-sr-only">Actions</span></th>
|
|
16
|
+
</tr>
|
|
17
|
+
</thead>
|
|
18
|
+
<tbody>
|
|
19
|
+
<% @groups.each do |group| %>
|
|
20
|
+
<tr>
|
|
21
|
+
<td class="sqw-monospace"><%= group.exception_class.presence || "—" %></td>
|
|
22
|
+
<td>
|
|
23
|
+
<% if group.sample_backtrace.present? %>
|
|
24
|
+
<details class="sqw-error-details">
|
|
25
|
+
<summary class="sqw-muted sqw-truncate" title="<%= group.message_prefix %>">
|
|
26
|
+
<%= group.message_prefix.presence || "—" %>
|
|
27
|
+
</summary>
|
|
28
|
+
<pre class="sqw-error-backtrace"><%= Array(group.sample_backtrace).first(10).join("\n") %></pre>
|
|
29
|
+
</details>
|
|
30
|
+
<% else %>
|
|
31
|
+
<span class="sqw-muted sqw-truncate" title="<%= group.message_prefix %>"><%= group.message_prefix.presence || "—" %></span>
|
|
32
|
+
<% end %>
|
|
33
|
+
</td>
|
|
34
|
+
<td><%= group.count %></td>
|
|
35
|
+
<td class="sqw-actions">
|
|
36
|
+
<%= link_to "View Jobs", failed_jobs_path(error_class: group.exception_class),
|
|
37
|
+
class: "sqw-btn sqw-btn--muted sqw-btn--sm" %>
|
|
38
|
+
</td>
|
|
39
|
+
</tr>
|
|
40
|
+
<% end %>
|
|
41
|
+
</tbody>
|
|
42
|
+
</table>
|
|
43
|
+
<% else %>
|
|
44
|
+
<div class="sqw-empty">
|
|
45
|
+
<p class="sqw-empty__title">No failed jobs</p>
|
|
46
|
+
<p class="sqw-empty__hint">All clear — your jobs are running without errors.</p>
|
|
47
|
+
</div>
|
|
48
|
+
<% end %>
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
<div class="sqw-page-header sqw-page-header--split">
|
|
2
|
-
<
|
|
2
|
+
<div>
|
|
3
|
+
<h1 class="sqw-page-title">Failed Jobs</h1>
|
|
4
|
+
<% if @error_class %>
|
|
5
|
+
<div class="sqw-breadcrumb">
|
|
6
|
+
Filtered by <span class="sqw-monospace"><%= @error_class %></span>
|
|
7
|
+
— <%= link_to "Clear filter", failed_jobs_path %>
|
|
8
|
+
</div>
|
|
9
|
+
<% end %>
|
|
10
|
+
</div>
|
|
3
11
|
<div class="sqw-header-actions">
|
|
12
|
+
<%= link_to "Error Summary", failed_job_errors_path, class: "sqw-btn sqw-btn--muted sqw-btn--sm" %>
|
|
4
13
|
<%= link_to "Export CSV", failed_jobs_path(format: :csv),
|
|
5
14
|
class: "sqw-btn sqw-btn--muted sqw-btn--sm", data: { turbo: false } %>
|
|
6
15
|
</div>
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
["avg", "Avg"],
|
|
13
13
|
["p50", "p50"],
|
|
14
14
|
["p95", "p95"],
|
|
15
|
+
["p99", "p99"],
|
|
16
|
+
["stddev", "Std Dev"],
|
|
15
17
|
["min", "Min"],
|
|
16
18
|
["max", "Max"]
|
|
17
19
|
].each do |col, label| %>
|
|
@@ -35,6 +37,8 @@
|
|
|
35
37
|
<td class="sqw-muted"><%= format_duration(row[:avg]) %></td>
|
|
36
38
|
<td class="sqw-muted"><%= format_duration(row[:p50]) %></td>
|
|
37
39
|
<td><strong><%= format_duration(row[:p95]) %></strong></td>
|
|
40
|
+
<td class="sqw-muted"><%= format_duration(row[:p99]) %></td>
|
|
41
|
+
<td class="sqw-muted"><%= format_duration(row[:stddev]) %></td>
|
|
38
42
|
<td class="sqw-muted"><%= format_duration(row[:min]) %></td>
|
|
39
43
|
<td class="sqw-muted"><%= format_duration(row[:max]) %></td>
|
|
40
44
|
</tr>
|
data/config/routes.rb
CHANGED
|
@@ -20,6 +20,8 @@ SolidStackWeb::Engine.routes.draw do
|
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
get "failed_jobs/errors", to: "failed_jobs/errors#index", as: :failed_job_errors
|
|
24
|
+
|
|
23
25
|
resources :failed_jobs, only: [:index, :show, :destroy] do
|
|
24
26
|
member { post :retry }
|
|
25
27
|
resource :arguments, only: [:update], controller: "failed_jobs/arguments"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: solid_stack_web
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chuck Smith
|
|
@@ -160,6 +160,7 @@ files:
|
|
|
160
160
|
- app/controllers/solid_stack_web/cache_entries_controller.rb
|
|
161
161
|
- app/controllers/solid_stack_web/dashboard_controller.rb
|
|
162
162
|
- app/controllers/solid_stack_web/failed_jobs/arguments_controller.rb
|
|
163
|
+
- app/controllers/solid_stack_web/failed_jobs/errors_controller.rb
|
|
163
164
|
- app/controllers/solid_stack_web/failed_jobs/selections_controller.rb
|
|
164
165
|
- app/controllers/solid_stack_web/failed_jobs_controller.rb
|
|
165
166
|
- app/controllers/solid_stack_web/history_controller.rb
|
|
@@ -187,6 +188,8 @@ files:
|
|
|
187
188
|
- app/models/solid_stack_web/cache_size_stats.rb
|
|
188
189
|
- app/models/solid_stack_web/cache_stats.rb
|
|
189
190
|
- app/models/solid_stack_web/cache_timeline.rb
|
|
191
|
+
- app/models/solid_stack_web/error_frequency_report.rb
|
|
192
|
+
- app/models/solid_stack_web/failed_job_sparkline.rb
|
|
190
193
|
- app/models/solid_stack_web/job.rb
|
|
191
194
|
- app/models/solid_stack_web/queue_depth_sparkline.rb
|
|
192
195
|
- app/models/solid_stack_web/queue_stats.rb
|
|
@@ -201,6 +204,7 @@ files:
|
|
|
201
204
|
- app/views/solid_stack_web/errors/internal_server_error.html.erb
|
|
202
205
|
- app/views/solid_stack_web/errors/not_found.html.erb
|
|
203
206
|
- app/views/solid_stack_web/failed_jobs/destroy.turbo_stream.erb
|
|
207
|
+
- app/views/solid_stack_web/failed_jobs/errors/index.html.erb
|
|
204
208
|
- app/views/solid_stack_web/failed_jobs/index.html.erb
|
|
205
209
|
- app/views/solid_stack_web/failed_jobs/show.html.erb
|
|
206
210
|
- app/views/solid_stack_web/history/index.html.erb
|