cosmonats 0.8.0 → 0.9.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 +21 -11
- data/lib/cosmo/api/counter.rb +1 -1
- data/lib/cosmo/api/cron/entry.rb +10 -11
- data/lib/cosmo/api/cron.rb +3 -3
- data/lib/cosmo/api/job.rb +1 -1
- data/lib/cosmo/api/kv.rb +3 -3
- data/lib/cosmo/api/stats/busy.rb +5 -0
- data/lib/cosmo/api/stats/metrics.rb +3 -3
- data/lib/cosmo/api/stats.rb +15 -0
- data/lib/cosmo/api/stream.rb +7 -2
- data/lib/cosmo/client.rb +10 -13
- data/lib/cosmo/job/data.rb +1 -1
- data/lib/cosmo/job/processor.rb +5 -6
- data/lib/cosmo/utils.rb +0 -1
- data/lib/cosmo/version.rb +1 -1
- data/lib/cosmo/web/assets/app.css +271 -629
- data/lib/cosmo/web/assets/app.js +158 -0
- data/lib/cosmo/web/assets/fonts/OFL.txt +92 -0
- data/lib/cosmo/web/assets/fonts/geist-mono.woff2 +0 -0
- data/lib/cosmo/web/assets/fonts/geist.woff2 +0 -0
- data/lib/cosmo/web/assets/logo.svg +10 -0
- data/lib/cosmo/web/cache.rb +75 -0
- data/lib/cosmo/web/chart.rb +62 -38
- data/lib/cosmo/web/circuit.rb +55 -0
- data/lib/cosmo/web/context.rb +0 -4
- data/lib/cosmo/web/controllers/application.rb +16 -0
- data/lib/cosmo/web/controllers/batches.rb +1 -0
- data/lib/cosmo/web/controllers/crons.rb +9 -2
- data/lib/cosmo/web/controllers/jobs.rb +50 -59
- data/lib/cosmo/web/controllers/metrics.rb +1 -0
- data/lib/cosmo/web/controllers/overview.rb +70 -0
- data/lib/cosmo/web/controllers/processes.rb +33 -0
- data/lib/cosmo/web/controllers/streams.rb +26 -28
- data/lib/cosmo/web/helpers/application.rb +57 -0
- data/lib/cosmo/web/renderer.rb +19 -6
- data/lib/cosmo/web/throughput.rb +104 -0
- data/lib/cosmo/web/views/batches/_table.erb +42 -54
- data/lib/cosmo/web/views/batches/index.erb +3 -10
- data/lib/cosmo/web/views/crons/_table.erb +32 -53
- data/lib/cosmo/web/views/crons/index.erb +2 -9
- data/lib/cosmo/web/views/jobs/_args.erb +5 -0
- data/lib/cosmo/web/views/jobs/_busy.erb +25 -121
- data/lib/cosmo/web/views/jobs/_dead.erb +42 -82
- data/lib/cosmo/web/views/jobs/_enqueued.erb +33 -113
- data/lib/cosmo/web/views/jobs/_panel.erb +29 -0
- data/lib/cosmo/web/views/jobs/_queue_row.erb +18 -0
- data/lib/cosmo/web/views/jobs/_queues.erb +20 -0
- data/lib/cosmo/web/views/jobs/_scheduled.erb +20 -54
- data/lib/cosmo/web/views/jobs/index.erb +4 -17
- data/lib/cosmo/web/views/layout.erb +73 -22
- data/lib/cosmo/web/views/metrics/_chart.erb +37 -32
- data/lib/cosmo/web/views/metrics/_panel.erb +3 -3
- data/lib/cosmo/web/views/metrics/_table.erb +24 -40
- data/lib/cosmo/web/views/metrics/index.erb +8 -16
- data/lib/cosmo/web/views/overview/_badges.erb +3 -0
- data/lib/cosmo/web/views/overview/_kpis.erb +19 -0
- data/lib/cosmo/web/views/overview/_running.erb +18 -0
- data/lib/cosmo/web/views/overview/_throughput.erb +50 -0
- data/lib/cosmo/web/views/overview/_workers.erb +25 -0
- data/lib/cosmo/web/views/overview/index.erb +28 -0
- data/lib/cosmo/web/views/processes/_table.erb +67 -0
- data/lib/cosmo/web/views/processes/index.erb +1 -0
- data/lib/cosmo/web/views/shared/_pager.erb +22 -0
- data/lib/cosmo/web/views/shared/unavailable.erb +7 -0
- data/lib/cosmo/web/views/streams/_info.erb +28 -86
- data/lib/cosmo/web/views/streams/_list.erb +7 -0
- data/lib/cosmo/web/views/streams/_pause_banner.erb +8 -16
- data/lib/cosmo/web/views/streams/_stream.erb +52 -0
- data/lib/cosmo/web/views/streams/index.erb +3 -11
- data/lib/cosmo/web/views/streams/info.erb +3 -11
- data/lib/cosmo/web.rb +66 -35
- data/sig/cosmo/api/cron/entry.rbs +2 -1
- data/sig/cosmo/api/stats/busy.rbs +2 -0
- data/sig/cosmo/api/stats/metrics.rbs +1 -1
- data/sig/cosmo/api/stats.rbs +4 -0
- data/sig/cosmo/api/stream.rbs +2 -0
- data/sig/cosmo/client.rbs +1 -0
- metadata +31 -18
- data/lib/cosmo/utils/overrides.rb +0 -206
- data/lib/cosmo/utils/warnings.rb +0 -17
- data/lib/cosmo/web/controllers/actions.rb +0 -16
- data/lib/cosmo/web/views/actions/index.erb +0 -7
- data/lib/cosmo/web/views/jobs/_processes.erb +0 -145
- data/lib/cosmo/web/views/jobs/_stats.erb +0 -77
- data/lib/cosmo/web/views/jobs/_tabs.erb +0 -14
- data/lib/cosmo/web/views/jobs/busy.erb +0 -18
- data/lib/cosmo/web/views/jobs/dead.erb +0 -18
- data/lib/cosmo/web/views/jobs/enqueued.erb +0 -18
- data/lib/cosmo/web/views/jobs/scheduled.erb +0 -18
- data/lib/cosmo/web/views/streams/_stream_row.erb +0 -42
- data/lib/cosmo/web/views/streams/_table.erb +0 -25
|
@@ -1,58 +1,46 @@
|
|
|
1
1
|
<% if @batches.empty? -%>
|
|
2
|
-
<
|
|
2
|
+
<section class="card"><div class="empty"><b>No batches tracked</b>Group jobs with <code>Cosmo::Batch.new.jobs { … }</code>.</div></section>
|
|
3
3
|
<% else -%>
|
|
4
|
-
<div class="
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
<% end -%>
|
|
40
|
-
</
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
<span class="text-muted">—</span>
|
|
46
|
-
<% end -%>
|
|
47
|
-
</td>
|
|
48
|
-
<td><%= format_numbers(stats[:total]) %></td>
|
|
49
|
-
<td><%= format_numbers(stats[:pending]) %></td>
|
|
50
|
-
<td><%= format_numbers(stats[:succeeded]) %></td>
|
|
51
|
-
<td><%= format_numbers(stats[:failed]) %></td>
|
|
52
|
-
<td><%= format_timestamp(b.created_at) %></td>
|
|
53
|
-
</tr>
|
|
54
|
-
<% end -%>
|
|
55
|
-
</tbody>
|
|
56
|
-
</table>
|
|
4
|
+
<div class="batches">
|
|
5
|
+
<% @batches.each do |b| -%>
|
|
6
|
+
<% stats = b.stats -%>
|
|
7
|
+
<% total = stats[:total].to_i -%>
|
|
8
|
+
<% succeeded = stats[:succeeded].to_i -%>
|
|
9
|
+
<% failed = stats[:failed].to_i -%>
|
|
10
|
+
<% done = total - stats[:pending].to_i -%>
|
|
11
|
+
<% status, tone = if !b.ready? then ['open', 'var(--acc)'] elsif failed.positive? then ['complete with failures', 'var(--warn)'] else ['success', 'var(--ok)'] end -%>
|
|
12
|
+
<% callbacks = b.callbacks.compact -%>
|
|
13
|
+
<section class="card batch">
|
|
14
|
+
<div class="batch-top">
|
|
15
|
+
<div class="stack" style="flex:1;gap:4px">
|
|
16
|
+
<div class="mono strong ellip"><%= h(b.bid) %></div>
|
|
17
|
+
<div class="small-mono ellip">
|
|
18
|
+
<% if b.parent_id -%>parent <%= h(b.parent_id) %> · <% end -%>started <%= b.created_at ? "#{elapsed(b.created_at)} ago" : '—' %>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
<span class="pill" style="--tone:<%= tone %>"><%= status %></span>
|
|
22
|
+
</div>
|
|
23
|
+
<div class="batch-pct">
|
|
24
|
+
<b><%= total.positive? ? (done * 100 / total).clamp(0, 100) : 0 %>%</b>
|
|
25
|
+
<span class="mono mute" style="font-size:11.5px"><%= compact_number(done) %> / <%= compact_number(total) %></span>
|
|
26
|
+
</div>
|
|
27
|
+
<div class="progress">
|
|
28
|
+
<% if total.positive? -%>
|
|
29
|
+
<i style="width:<%= (succeeded * 100.0 / total).round(2) %>%;background:<%= hue(b.bid) %>"></i>
|
|
30
|
+
<% if failed.positive? -%><i style="width:<%= [failed * 100.0 / total, 0.6].max.round(2) %>%;background:var(--err)"></i><% end -%>
|
|
31
|
+
<% end -%>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="batch-foot">
|
|
34
|
+
<span><span class="num" style="color:var(--ink)"><%= format_numbers(succeeded) %></span> succeeded</span>
|
|
35
|
+
<span><span class="num" style="color:var(--err)"><%= format_numbers(failed) %></span> failed</span>
|
|
36
|
+
<span><span class="num" style="color:var(--ink)"><%= format_numbers(stats[:pending]) %></span> pending</span>
|
|
37
|
+
<% if callbacks.any? -%>
|
|
38
|
+
<span class="cb">
|
|
39
|
+
<% callbacks.each do |event, callback| -%><span>on_<%= event %> → <%= h(callback[:class]) %></span><% end -%>
|
|
40
|
+
</span>
|
|
41
|
+
<% end -%>
|
|
42
|
+
</div>
|
|
43
|
+
</section>
|
|
44
|
+
<% end -%>
|
|
57
45
|
</div>
|
|
58
46
|
<% end -%>
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
<
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
<div id="batches-table"
|
|
5
|
-
hx-get="<%= url_for('/batches/_table') %>"
|
|
6
|
-
hx-trigger="load, every <%= poll_interval %>s"
|
|
7
|
-
hx-swap="innerHTML">
|
|
8
|
-
<div class="alert alert-info">Loading batches…</div>
|
|
9
|
-
</div>
|
|
10
|
-
</section>
|
|
1
|
+
<div hx-get="<%= url_for('/batches/_table') %>" hx-trigger="load, cosmo:poll" data-poll="<%= poll_interval %>" hx-swap="innerHTML">
|
|
2
|
+
<section class="card"><div class="loading">Loading batches…</div></section>
|
|
3
|
+
</div>
|
|
@@ -1,58 +1,37 @@
|
|
|
1
1
|
<% if @schedules.empty? -%>
|
|
2
|
-
<div class="
|
|
3
|
-
No cron schedules deployed in NATS
|
|
4
|
-
Add a <code>cron:</code> section to <code>cosmo.yml</code> and run
|
|
5
|
-
<code>cosmo --setup</code> to publish them.
|
|
2
|
+
<div class="empty">
|
|
3
|
+
<b>No cron schedules deployed in NATS</b>
|
|
4
|
+
<span>Add a <code>cron:</code> section to <code>cosmo.yml</code> and run <code>cosmo --setup</code> to publish them.</span>
|
|
6
5
|
</div>
|
|
7
6
|
<% else -%>
|
|
8
|
-
<div class="
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
<% end -%>
|
|
28
|
-
<div style="font-size:0.75em; color:#888; margin-top:2px;">
|
|
29
|
-
<code><%= h(s[:schedule_subject].to_s) %></code>
|
|
30
|
-
</div>
|
|
31
|
-
</td>
|
|
32
|
-
<td><code class="subject-tag"><%= h(s[:schedule].to_s) %></code></td>
|
|
33
|
-
<td><%= h(s[:timezone] || "UTC") %></td>
|
|
34
|
-
<td>
|
|
35
|
-
<% if s[:args]&.any? -%>
|
|
36
|
-
<code><%= h(s[:args].inspect) %></code>
|
|
37
|
-
<% else -%>
|
|
38
|
-
<span class="text-muted">—</span>
|
|
39
|
-
<% end -%>
|
|
40
|
-
</td>
|
|
41
|
-
<td><code><%= h(s[:stream].to_s) %></code></td>
|
|
42
|
-
<td style="white-space:nowrap;">
|
|
43
|
-
<a hx-post="<%= url_for('/crons/run', { subject: u(s[:schedule_subject].to_s) }) %>"
|
|
44
|
-
class="btn btn-success btn-sm"
|
|
45
|
-
title="Dispatch this job now, bypassing the schedule timer">► Run Now</a>
|
|
46
|
-
<a hx-delete="<%= url_for('/crons/delete', { subject: u(s[:schedule_subject].to_s) }) %>"
|
|
47
|
-
hx-target="#crons-table"
|
|
48
|
-
hx-swap="innerHTML"
|
|
49
|
-
hx-confirm="Delete '<%= h(s[:schedule_subject].to_s) %>'? NATS will stop firing it."
|
|
50
|
-
class="btn btn-danger btn-sm"
|
|
51
|
-
title="Purge schedule from NATS">🗑 Delete</a>
|
|
52
|
-
</td>
|
|
53
|
-
</tr>
|
|
7
|
+
<div class="scroll"><div style="min-width:900px">
|
|
8
|
+
<div class="thead t-crons"><span>Schedule</span><span>Cron</span><span>Stream</span><span>Args</span><span></span></div>
|
|
9
|
+
<% @schedules.each do |s| -%>
|
|
10
|
+
<% subject = s[:schedule_subject].to_s -%>
|
|
11
|
+
<div class="trow hover t-crons">
|
|
12
|
+
<div class="stack">
|
|
13
|
+
<span class="strong ellip"><%= h(s[:name] || s[:class]) %></span>
|
|
14
|
+
<% if s[:name] -%><span class="small-mono ellip"><%= h(s[:class]) %></span><% end -%>
|
|
15
|
+
<span class="small-mono ellip" title="<%= h(subject) %>"><%= h(subject) %></span>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="stack">
|
|
18
|
+
<span class="mono" style="font-size:12.5px"><%= h(s[:schedule]) %></span>
|
|
19
|
+
<span class="mute" style="font-size:11.5px"><%= h(s[:timezone] || 'UTC') %></span>
|
|
20
|
+
</div>
|
|
21
|
+
<span class="qname"><span class="dot" style="background:<%= hue(s[:stream]) %>"></span><span class="mono ellip"><%= h(s[:stream]) %></span></span>
|
|
22
|
+
<% if s[:args]&.any? -%>
|
|
23
|
+
<span class="mono mute ellip" style="font-size:11.5px" title="<%= h(s[:args].inspect) %>"><%= h(s[:args].inspect) %></span>
|
|
24
|
+
<% else -%>
|
|
25
|
+
<span class="faint">—</span>
|
|
54
26
|
<% end -%>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
27
|
+
<div class="btns">
|
|
28
|
+
<button type="button" class="btn" title="Dispatch this job now, bypassing the schedule timer"
|
|
29
|
+
hx-post="<%= url_for('/crons/run', { subject: u(subject) }) %>" hx-swap="none" hx-indicator="#global-spinner">► Run now</button>
|
|
30
|
+
<button type="button" class="btn danger" title="Purge schedule from NATS"
|
|
31
|
+
hx-delete="<%= url_for('/crons/delete', { subject: u(subject) }) %>" hx-target="#crons-table" hx-swap="innerHTML"
|
|
32
|
+
hx-indicator="#global-spinner" hx-confirm="Delete '<%= h(subject) %>'? NATS will stop firing it.">Delete</button>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
<% end -%>
|
|
36
|
+
</div></div>
|
|
58
37
|
<% end -%>
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
<section>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
<div id="crons-table"
|
|
5
|
-
hx-get="<%= url_for('/crons/_table') %>"
|
|
6
|
-
hx-trigger="load, every 10s"
|
|
7
|
-
hx-swap="innerHTML">
|
|
8
|
-
<div class="alert alert-info">Loading cron schedules…</div>
|
|
9
|
-
</div>
|
|
1
|
+
<section class="card" id="crons-table" hx-get="<%= url_for('/crons/_table') %>" hx-trigger="load, cosmo:poll" data-poll="10" hx-swap="innerHTML">
|
|
2
|
+
<div class="loading">Loading cron schedules…</div>
|
|
10
3
|
</section>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<% json = (JSON.generate(@args) rescue @args.to_s) -%>
|
|
2
|
+
<details class="stack">
|
|
3
|
+
<summary class="mono mute ellip" style="font-size:11.5px" title="<%= h(json) %>"><%= h(json) %></summary>
|
|
4
|
+
<pre class="code-block"><%= h((JSON.pretty_generate(@args) rescue @args.to_s)) %></pre>
|
|
5
|
+
</details>
|
|
@@ -1,121 +1,25 @@
|
|
|
1
|
-
<%
|
|
2
|
-
<div
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
</span>
|
|
27
|
-
</div>
|
|
28
|
-
|
|
29
|
-
<% if @jobs.empty? -%>
|
|
30
|
-
<div class="alert alert-success">No jobs are currently running</div>
|
|
31
|
-
<% else -%>
|
|
32
|
-
<div class="table-container">
|
|
33
|
-
<table>
|
|
34
|
-
<thead>
|
|
35
|
-
<tr>
|
|
36
|
-
<th>Job</th>
|
|
37
|
-
<th>JID[Args]</th>
|
|
38
|
-
<th>Stream</th>
|
|
39
|
-
<th>Worker</th>
|
|
40
|
-
<th>Attempt</th>
|
|
41
|
-
<th>Running for</th>
|
|
42
|
-
</tr>
|
|
43
|
-
</thead>
|
|
44
|
-
<tbody>
|
|
45
|
-
<% @jobs.each do |job| -%>
|
|
46
|
-
<tr>
|
|
47
|
-
<td>
|
|
48
|
-
<div class="job-class">
|
|
49
|
-
<%= h(job.dig(:data, :class).to_s) %>
|
|
50
|
-
</div>
|
|
51
|
-
</td>
|
|
52
|
-
<td>
|
|
53
|
-
<div class="job-id">
|
|
54
|
-
<details>
|
|
55
|
-
<summary>
|
|
56
|
-
<code><%= h(job.dig(:data, :jid)) %></code>
|
|
57
|
-
</summary>
|
|
58
|
-
<code><%= h(JSON.pretty_generate(job.dig(:data, :args))) %></code>
|
|
59
|
-
</details>
|
|
60
|
-
</div>
|
|
61
|
-
</td>
|
|
62
|
-
<td>
|
|
63
|
-
<code><%= h(job[:stream].to_s) %></code>
|
|
64
|
-
</td>
|
|
65
|
-
<td><code style="font-size: var(--font-size-small);"><%= h(job[:worker].to_s) %></code></td>
|
|
66
|
-
<td>
|
|
67
|
-
<% delivery = job[:delivery].to_i -%>
|
|
68
|
-
<% if delivery > 1 -%>
|
|
69
|
-
<span class="badge <%= delivery >= 5 ? 'badge-danger' : 'badge-warning' %>"><%= delivery %></span>
|
|
70
|
-
<% else -%>
|
|
71
|
-
<span class="text-muted"><%= delivery.zero? ? '—' : delivery %></span>
|
|
72
|
-
<% end -%>
|
|
73
|
-
</td>
|
|
74
|
-
<td>
|
|
75
|
-
<%= elapsed(job[:started_at]) %>
|
|
76
|
-
</td>
|
|
77
|
-
</tr>
|
|
78
|
-
<% end -%>
|
|
79
|
-
</tbody>
|
|
80
|
-
</table>
|
|
81
|
-
</div>
|
|
82
|
-
<% end -%>
|
|
83
|
-
|
|
84
|
-
<% if @total_pages.to_i > 1 -%>
|
|
85
|
-
<div class="pagination">
|
|
86
|
-
<% if @page > 1 -%>
|
|
87
|
-
<a hx-get="<%= url_for('/jobs/_busy', nav.merge(page: @page - 1)) %>"
|
|
88
|
-
hx-target="#busy-poller"
|
|
89
|
-
hx-swap="outerHTML"
|
|
90
|
-
hx-indicator="#global-spinner"
|
|
91
|
-
class="btn">← Prev</a>
|
|
92
|
-
<% else -%>
|
|
93
|
-
<span class="btn btn-disabled">← Prev</span>
|
|
94
|
-
<% end -%>
|
|
95
|
-
|
|
96
|
-
<% pages(@page, @total_pages).each do |p| -%>
|
|
97
|
-
<% if p == :gap -%>
|
|
98
|
-
<span class="pagination-gap">…</span>
|
|
99
|
-
<% elsif p == @page -%>
|
|
100
|
-
<span class="btn btn-primary btn-disabled"><%= p %></span>
|
|
101
|
-
<% else -%>
|
|
102
|
-
<a hx-get="<%= url_for('/jobs/_busy', nav.merge(page: p)) %>"
|
|
103
|
-
hx-target="#busy-poller"
|
|
104
|
-
hx-swap="outerHTML"
|
|
105
|
-
hx-indicator="#global-spinner"
|
|
106
|
-
class="btn"><%= p %></a>
|
|
107
|
-
<% end -%>
|
|
108
|
-
<% end -%>
|
|
109
|
-
|
|
110
|
-
<% if @page < @total_pages -%>
|
|
111
|
-
<a hx-get="<%= url_for('/jobs/_busy', nav.merge(page: @page + 1)) %>"
|
|
112
|
-
hx-target="#busy-poller"
|
|
113
|
-
hx-swap="outerHTML"
|
|
114
|
-
hx-indicator="#global-spinner"
|
|
115
|
-
class="btn">Next →</a>
|
|
116
|
-
<% else -%>
|
|
117
|
-
<span class="btn btn-disabled">Next →</span>
|
|
118
|
-
<% end -%>
|
|
119
|
-
</div>
|
|
120
|
-
<% end -%>
|
|
121
|
-
</div>
|
|
1
|
+
<% if @jobs.empty? -%>
|
|
2
|
+
<div class="empty"><b>All quiet</b>No jobs are running right now.</div>
|
|
3
|
+
<% else -%>
|
|
4
|
+
<div class="scroll"><div style="min-width:860px">
|
|
5
|
+
<div class="thead t-jobs"><span>JID</span><span>Class</span><span>Args</span><span>Queue</span><span>Status</span><span class="r">Try</span><span class="r">Running for</span></div>
|
|
6
|
+
<% @jobs.each do |job| -%>
|
|
7
|
+
<% delivery = job[:delivery].to_i -%>
|
|
8
|
+
<% retries = job.dig(:data, :retry) -%>
|
|
9
|
+
<div class="trow hover t-jobs">
|
|
10
|
+
<span class="mono mute ellip" style="font-size:11.5px" title="<%= h(job.dig(:data, :jid)) %>"><%= h(job.dig(:data, :jid)) %></span>
|
|
11
|
+
<div class="stack">
|
|
12
|
+
<span class="strong ellip"><%= h(job.dig(:data, :class)) %></span>
|
|
13
|
+
<span class="small-mono ellip" title="<%= h(job[:worker]) %>"><%= h(job[:worker]) %></span>
|
|
14
|
+
</div>
|
|
15
|
+
<%= render('jobs/_args', { args: job.dig(:data, :args) }) %>
|
|
16
|
+
<span class="mono ellip" style="font-size:12px"><%= h(job[:stream]) %></span>
|
|
17
|
+
<span class="pill" style="--tone:var(--acc)">running</span>
|
|
18
|
+
<span class="mono r" style="font-size:12px;color:<%= delivery >= 5 ? 'var(--err)' : delivery > 1 ? 'var(--warn)' : 'var(--mute)' %>">
|
|
19
|
+
<%= delivery.zero? ? '—' : delivery %><%= "/#{retries.to_i + 1}" if retries.is_a?(Integer) && delivery.positive? %>
|
|
20
|
+
</span>
|
|
21
|
+
<span class="mono mute r" style="font-size:11.5px"><%= elapsed(job[:started_at]) %></span>
|
|
22
|
+
</div>
|
|
23
|
+
<% end -%>
|
|
24
|
+
</div></div>
|
|
25
|
+
<% end -%>
|
|
@@ -1,83 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
<div class="
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
</
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
</
|
|
38
|
-
<td style="min-width: 420px;">
|
|
39
|
-
<% if job.error? -%>
|
|
40
|
-
<div class="error-message">
|
|
41
|
-
<strong><%= h(job.error_class.to_s.empty? ? "Error" : job.error_class) %></strong>
|
|
42
|
-
<p><%= h(job.error_message) %></p>
|
|
43
|
-
<% if job.error_backtrace -%>
|
|
44
|
-
<details>
|
|
45
|
-
<summary>Backtrace</summary>
|
|
46
|
-
<code><%= h(job.error_backtrace_lines.join("\n")) %></code>
|
|
47
|
-
</details>
|
|
48
|
-
<% end -%>
|
|
49
|
-
</div>
|
|
50
|
-
<% else -%>
|
|
51
|
-
<div class="error-message">
|
|
52
|
-
<strong>Unknown</strong>
|
|
53
|
-
<p>No error recorded for this job</p>
|
|
54
|
-
</div>
|
|
55
|
-
<% end -%>
|
|
56
|
-
</td>
|
|
57
|
-
<td style="white-space: nowrap;">
|
|
58
|
-
<% if (failed_at = job.stored_at) -%>
|
|
59
|
-
<time datetime="<%= h(job.timestamp) %>" title="<%= h(job.timestamp) %>"><%= h(format_timestamp(failed_at)) %></time>
|
|
60
|
-
<div class="text-muted" style="font-size: var(--font-size-small);"><%= elapsed(failed_at) %> ago</div>
|
|
61
|
-
<% else -%>
|
|
62
|
-
<span class="text-muted">—</span>
|
|
63
|
-
<% end -%>
|
|
64
|
-
</td>
|
|
65
|
-
<td style="white-space: nowrap;">
|
|
66
|
-
<a hx-patch="<%= url_for("/jobs/retry/#{job.seq}") %>"
|
|
67
|
-
hx-target="#dead-row-<%= job.seq %>"
|
|
68
|
-
hx-swap="innerHTML"
|
|
69
|
-
title="Retry"
|
|
70
|
-
class="btn btn-primary btn-sm">↻</a>
|
|
71
|
-
<a hx-delete="<%= url_for("/jobs/delete/#{job.seq}") %>"
|
|
72
|
-
hx-target="#dead-row-<%= job.seq %>"
|
|
73
|
-
hx-swap="innerHTML"
|
|
74
|
-
title="Delete"
|
|
75
|
-
class="btn btn-danger btn-sm">✕</a>
|
|
76
|
-
</td>
|
|
77
|
-
</tr>
|
|
1
|
+
<% if @jobs.empty? -%>
|
|
2
|
+
<div class="empty"><b>No dead jobs</b>Nothing has given up.</div>
|
|
3
|
+
<% else -%>
|
|
4
|
+
<div class="scroll"><div style="min-width:860px">
|
|
5
|
+
<div class="thead t-jobs actions"><span>JID</span><span>Class</span><span>Args</span><span>Queue</span><span>Status</span><span class="r">Failed</span><span></span></div>
|
|
6
|
+
<% @jobs.each do |job| -%>
|
|
7
|
+
<div class="trow hover t-jobs actions">
|
|
8
|
+
<span class="mono mute ellip" style="font-size:11.5px" title="<%= h(job.data[:jid]) %>"><%= h(job.data[:jid]) %></span>
|
|
9
|
+
<span class="strong ellip"><%= h(job.data[:class]) %></span>
|
|
10
|
+
<%= render('jobs/_args', { args: job.data[:args] }) %>
|
|
11
|
+
<span class="mono ellip" style="font-size:12px"><%= job.x_stream ? h(job.x_stream) : '—' %></span>
|
|
12
|
+
<span class="pill" style="--tone:var(--err)">dead</span>
|
|
13
|
+
<% if (failed_at = job.stored_at) -%>
|
|
14
|
+
<span class="mono mute r" style="font-size:11.5px" title="<%= h(format_timestamp(failed_at)) %>"><%= elapsed(failed_at) %> ago</span>
|
|
15
|
+
<% else -%>
|
|
16
|
+
<span class="mono mute r">—</span>
|
|
17
|
+
<% end -%>
|
|
18
|
+
<div class="btns">
|
|
19
|
+
<button type="button" class="btn" title="Retry"
|
|
20
|
+
hx-patch="<%= url_for("/jobs/retry/#{job.seq}") %>" hx-target="closest .trow" hx-swap="outerHTML"
|
|
21
|
+
hx-indicator="#global-spinner">↻</button>
|
|
22
|
+
<button type="button" class="btn danger" title="Delete"
|
|
23
|
+
hx-delete="<%= url_for("/jobs/delete/#{job.seq}") %>" hx-target="closest .trow" hx-swap="outerHTML"
|
|
24
|
+
hx-indicator="#global-spinner" hx-confirm="Delete dead job #<%= job.seq %>?">✕</button>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="full error-line">
|
|
27
|
+
<% if job.error? -%>
|
|
28
|
+
<b><%= h(job.error_class.to_s.empty? ? 'Error' : job.error_class) %></b>
|
|
29
|
+
<span><%= h(job.error_message) %></span>
|
|
30
|
+
<% if job.error_backtrace -%>
|
|
31
|
+
<details>
|
|
32
|
+
<summary class="small-mono">Backtrace</summary>
|
|
33
|
+
<pre class="code-block"><%= h(job.error_backtrace_lines.join("\n")) %></pre>
|
|
34
|
+
</details>
|
|
35
|
+
<% end -%>
|
|
36
|
+
<% else -%>
|
|
37
|
+
<b>Unknown</b><span class="mute">No error recorded for this job</span>
|
|
78
38
|
<% end -%>
|
|
79
|
-
</
|
|
80
|
-
</
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
<% end -%>
|
|
42
|
+
</div></div>
|
|
43
|
+
<% end -%>
|