cosmonats 0.7.0 → 0.8.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 +199 -103
- data/lib/cosmo/active_job/adapter.rb +1 -2
- data/lib/cosmo/api/counter.rb +16 -16
- data/lib/cosmo/api/cron.rb +3 -0
- data/lib/cosmo/api/registry.rb +1 -1
- data/lib/cosmo/api/stats/busy.rb +1 -1
- data/lib/cosmo/api/stats/metrics.rb +109 -0
- data/lib/cosmo/api/stats/processes.rb +14 -4
- data/lib/cosmo/api/stats/totals.rb +4 -12
- data/lib/cosmo/api/stats.rb +3 -2
- data/lib/cosmo/batch/counters.rb +19 -0
- data/lib/cosmo/batch.rb +6 -6
- data/lib/cosmo/cli.rb +21 -5
- data/lib/cosmo/client.rb +28 -2
- data/lib/cosmo/config/cosmo.yml +58 -0
- data/lib/cosmo/config/settings.rb +20 -0
- data/lib/cosmo/config.rb +144 -19
- data/lib/cosmo/heartbeat.rb +11 -0
- data/lib/cosmo/job/data.rb +17 -12
- data/lib/cosmo/job/enqueuer.rb +53 -0
- data/lib/cosmo/job/limit.rb +2 -2
- data/lib/cosmo/job/processor.rb +27 -52
- data/lib/cosmo/job/requeue.rb +20 -0
- data/lib/cosmo/job.rb +8 -22
- data/lib/cosmo/logger.rb +15 -2
- data/lib/cosmo/middleware/busy.rb +15 -0
- data/lib/cosmo/middleware/chain.rb +137 -0
- data/lib/cosmo/middleware/limit.rb +50 -0
- data/lib/cosmo/middleware/metrics.rb +39 -0
- data/lib/cosmo/{sentry/job_processor_middleware.rb → middleware/sentry.rb} +26 -30
- data/lib/cosmo/middleware/totals.rb +29 -0
- data/lib/cosmo/middleware.rb +7 -0
- data/lib/cosmo/processor.rb +11 -5
- data/lib/cosmo/publisher.rb +1 -13
- data/lib/cosmo/railtie.rb +2 -2
- data/lib/cosmo/services.rb +69 -0
- data/lib/cosmo/stream/processor.rb +7 -1
- data/lib/cosmo/utils/hash.rb +7 -8
- data/lib/cosmo/version.rb +1 -1
- data/lib/cosmo/web/assets/app.css +58 -0
- data/lib/cosmo/web/chart.rb +98 -0
- data/lib/cosmo/web/controllers/crons.rb +8 -0
- data/lib/cosmo/web/controllers/jobs.rb +16 -2
- data/lib/cosmo/web/controllers/metrics.rb +48 -0
- data/lib/cosmo/web/helpers/application.rb +10 -0
- data/lib/cosmo/web/views/batches/_table.erb +11 -0
- data/lib/cosmo/web/views/jobs/_processes.erb +49 -35
- data/lib/cosmo/web/views/jobs/_stats.erb +4 -0
- data/lib/cosmo/web/views/jobs/_tabs.erb +6 -0
- data/lib/cosmo/web/views/jobs/index.erb +1 -1
- data/lib/cosmo/web/views/layout.erb +1 -1
- data/lib/cosmo/web/views/metrics/_chart.erb +36 -0
- data/lib/cosmo/web/views/metrics/_panel.erb +13 -0
- data/lib/cosmo/web/views/metrics/_table.erb +41 -0
- data/lib/cosmo/web/views/metrics/index.erb +17 -0
- data/lib/cosmo/web.rb +3 -0
- data/lib/cosmo.rb +38 -0
- data/sig/cosmo/api/counter.rbs +2 -3
- data/sig/cosmo/api/stats/busy.rbs +1 -1
- data/sig/cosmo/api/stats/metrics.rbs +42 -0
- data/sig/cosmo/api/stats/processes.rbs +3 -0
- data/sig/cosmo/api/stats/totals.rbs +3 -2
- data/sig/cosmo/batch/counters.rbs +10 -0
- data/sig/cosmo/batch.rbs +3 -4
- data/sig/cosmo/cli.rbs +2 -0
- data/sig/cosmo/client.rbs +3 -1
- data/sig/cosmo/config/settings.rbs +31 -0
- data/sig/cosmo/config.rbs +59 -5
- data/sig/cosmo/heartbeat.rbs +2 -0
- data/sig/cosmo/job/data.rbs +5 -3
- data/sig/cosmo/job/enqueuer.rbs +11 -0
- data/sig/cosmo/job/processor.rbs +3 -5
- data/sig/cosmo/job/requeue.rbs +9 -0
- data/sig/cosmo/job.rbs +0 -2
- data/sig/cosmo/logger.rbs +6 -0
- data/sig/cosmo/middleware/chain.rbs +51 -0
- data/sig/cosmo/middleware/sentry.rbs +14 -0
- data/sig/cosmo/middleware.rbs +35 -0
- data/sig/cosmo/processor.rbs +2 -0
- data/sig/cosmo/publisher.rbs +1 -9
- data/sig/cosmo/services.rbs +18 -0
- data/sig/cosmo/stream/processor.rbs +2 -0
- data/sig/cosmo/utils/hash.rbs +2 -3
- data/sig/cosmo.rbs +11 -0
- metadata +30 -3
- data/lib/cosmo/sentry/auto.rb +0 -6
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<% nav = @polling ? { limit: @limit } : { limit: @limit, poll: 0 } -%>
|
|
2
2
|
<% states = { "running" => "badge-success", "quiet" => "badge-warning", "stopping" => "badge-danger" } -%>
|
|
3
|
+
<% kinds = { "jobs" => "Jobs", "streams" => "Streams", "jobs+streams" => "Jobs & Streams", "none" => "No subscriptions" } -%>
|
|
3
4
|
<div id="processes-poller"
|
|
4
5
|
<% if @polling -%>
|
|
5
6
|
hx-get="<%= url_for('/jobs/_processes', page: @page, limit: @limit) %>"
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
</span>
|
|
28
29
|
</div>
|
|
29
30
|
|
|
30
|
-
<% if @
|
|
31
|
+
<% if @groups.empty? -%>
|
|
31
32
|
<div class="alert alert-warning">No worker processes are running</div>
|
|
32
33
|
<% else -%>
|
|
33
34
|
<div class="table-container">
|
|
@@ -47,44 +48,57 @@
|
|
|
47
48
|
</tr>
|
|
48
49
|
</thead>
|
|
49
50
|
<tbody>
|
|
50
|
-
<% @
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
51
|
+
<% @groups.each do |kind, group| -%>
|
|
52
|
+
<tr class="group-row">
|
|
53
|
+
<td colspan="10"><%= h(kinds.fetch(kind, kind)) %> <span class="text-muted">(<%= group.size %>)</span></td>
|
|
54
|
+
</tr>
|
|
55
|
+
<% group.each do |process| -%>
|
|
56
|
+
<% nats = Hash(process[:nats]) -%>
|
|
57
|
+
<tr>
|
|
58
|
+
<td>
|
|
59
|
+
<div class="job-class"><%= h(process[:hostname]) %>:<%= h(process[:pid]) %></div>
|
|
60
|
+
<div class="text-muted" style="font-size: var(--font-size-small);">
|
|
61
|
+
<%= h(nats[:ip] || "unknown IP") %>
|
|
62
|
+
<% if nats[:name] -%>· <code><%= h(nats[:name]) %></code><% end -%>
|
|
63
|
+
</div>
|
|
64
|
+
</td>
|
|
65
|
+
<td>
|
|
66
|
+
<% cmdline = process[:cmdline].to_s -%>
|
|
67
|
+
<div class="job-id">
|
|
68
|
+
<% if cmdline.length > 32 -%>
|
|
69
|
+
<details>
|
|
70
|
+
<summary>
|
|
71
|
+
<code class="truncate" title="<%= h(cmdline) %>"><%= h(cmdline) %></code>
|
|
72
|
+
</summary>
|
|
73
|
+
<code style="font-size: var(--font-size-small);"><%= h(cmdline) %></code>
|
|
74
|
+
</details>
|
|
75
|
+
<% else -%>
|
|
76
|
+
<code style="font-size: var(--font-size-small);"><%= h(cmdline) %></code>
|
|
77
|
+
<% end -%>
|
|
68
78
|
<div class="text-muted" style="font-size: var(--font-size-small);">
|
|
69
79
|
cosmo <%= h(process[:version]) %> · <%= h(process[:ruby]) %>
|
|
70
80
|
<% if process[:http_port] -%>· http :<%= h(process[:http_port]) %><% end -%>
|
|
71
81
|
</div>
|
|
72
|
-
</
|
|
73
|
-
</
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
</div>
|
|
83
|
+
</td>
|
|
84
|
+
<td>
|
|
85
|
+
<% Hash(process[:subscriptions]).each do |type, names| -%>
|
|
86
|
+
<% next if Array(names).empty? -%>
|
|
87
|
+
<div>
|
|
88
|
+
<% if kind == "jobs+streams" -%><span class="text-muted"><%= h(type) %>:</span><% end -%>
|
|
89
|
+
<code><%= h(Array(names).join(", ")) %></code>
|
|
90
|
+
</div>
|
|
91
|
+
<% end -%>
|
|
92
|
+
</td>
|
|
93
|
+
<td><%= h(process[:busy]) %> / <%= h(process[:concurrency]) %></td>
|
|
94
|
+
<td><span class="badge <%= states.fetch(process[:state].to_s, 'badge-info') %>"><%= h(process[:state]) %></span></td>
|
|
95
|
+
<td><%= nats[:rtt] ? "#{h(nats[:rtt])}ms" : '—' %></td>
|
|
96
|
+
<td><%= h(nats[:reconnects].to_i) %></td>
|
|
97
|
+
<td><%= process[:rss] ? format_bytes(process[:rss].to_i * 1024) : '—' %></td>
|
|
98
|
+
<td><%= elapsed(process[:started_at]) %></td>
|
|
99
|
+
<td><%= elapsed(process[:beat_at]) %> ago</td>
|
|
100
|
+
</tr>
|
|
101
|
+
<% end -%>
|
|
88
102
|
<% end -%>
|
|
89
103
|
</tbody>
|
|
90
104
|
</table>
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
class="<%= 'active' if referrer?('/jobs/enqueued') %>">📬 Enqueued</a>
|
|
35
35
|
</p>
|
|
36
36
|
</article>
|
|
37
|
+
<% if Cosmo::Config.scheduled.enabled -%>
|
|
37
38
|
<article class="stat-card">
|
|
38
39
|
<h3><%= format_numbers(@scheduled) %></h3>
|
|
39
40
|
<p>
|
|
@@ -45,6 +46,8 @@
|
|
|
45
46
|
class="<%= 'active' if referrer?('/jobs/scheduled') %>">⏰ Scheduled</a>
|
|
46
47
|
</p>
|
|
47
48
|
</article>
|
|
49
|
+
<% end -%>
|
|
50
|
+
<% if Cosmo::Config.dead.enabled -%>
|
|
48
51
|
<article class="stat-card">
|
|
49
52
|
<h3><%= format_numbers(@dead) %></h3>
|
|
50
53
|
<p>
|
|
@@ -56,6 +59,7 @@
|
|
|
56
59
|
class="<%= 'active' if referrer?('/jobs/dead') %>">💀 Dead</a>
|
|
57
60
|
</p>
|
|
58
61
|
</article>
|
|
62
|
+
<% end -%>
|
|
59
63
|
</div>
|
|
60
64
|
|
|
61
65
|
<script>
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
class="<%= 'active' if path_prefix?('/jobs') %>">📋 Dashboard</a>
|
|
4
4
|
<a href="<%= url_for('/batches') %>"
|
|
5
5
|
class="<%= 'active' if path_prefix?('/batches') %>">📦 Batches</a>
|
|
6
|
+
<% if Cosmo::Config.scheduled.enabled -%>
|
|
6
7
|
<a href="<%= url_for('/crons') %>"
|
|
7
8
|
class="<%= 'active' if path_prefix?('/crons') %>">⏰ Crons</a>
|
|
9
|
+
<% end -%>
|
|
10
|
+
<% if Cosmo::Config.metrics.enabled -%>
|
|
11
|
+
<a href="<%= url_for('/metrics') %>"
|
|
12
|
+
class="<%= 'active' if path_prefix?('/metrics') %>">📊 Metrics</a>
|
|
13
|
+
<% end -%>
|
|
8
14
|
</nav>
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
<div id="content">
|
|
11
11
|
<div id="processes-poller"
|
|
12
|
-
hx-get="<%= url_for('/jobs/_processes') %>"
|
|
12
|
+
hx-get="<%= url_for('/jobs/_processes', (@processes_query unless @processes_query.empty?)) %>"
|
|
13
13
|
hx-trigger="load"
|
|
14
14
|
hx-swap="outerHTML">
|
|
15
15
|
<div class="alert alert-info">Loading processes…</div>
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<a href="<%= url_for('/jobs') %>" class="navbar-brand">🚀 Cosmo</a>
|
|
15
15
|
<ul class="nav-list">
|
|
16
16
|
<li>
|
|
17
|
-
<a href="<%= url_for('/jobs') %>" class="<%= "active" if path_prefix?('/jobs', '/crons', '/batches') %>">Jobs</a>
|
|
17
|
+
<a href="<%= url_for('/jobs') %>" class="<%= "active" if path_prefix?('/jobs', '/crons', '/batches', '/metrics') %>">Jobs</a>
|
|
18
18
|
</li>
|
|
19
19
|
<li>
|
|
20
20
|
<a href="<%= url_for('/streams') %>" class="<%= "active" if current_page?('/streams') %>">Streams</a>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<div class="metrics-chart">
|
|
2
|
+
<div class="metrics-chart-header">
|
|
3
|
+
<span><code><%= h(@job) %></code> <span class="text-muted">last <%= @days %> days</span></span>
|
|
4
|
+
<span class="metrics-legend">
|
|
5
|
+
<span class="metrics-legend-runs">runs per day</span>
|
|
6
|
+
<span class="metrics-legend-exec">avg execution (ms)</span>
|
|
7
|
+
</span>
|
|
8
|
+
</div>
|
|
9
|
+
<% if @chart.empty? -%>
|
|
10
|
+
<div class="alert alert-info">No runs of <code><%= h(@job) %></code> in this period.</div>
|
|
11
|
+
<% else -%>
|
|
12
|
+
<svg viewBox="0 0 <%= Cosmo::Web::Chart::WIDTH %> <%= Cosmo::Web::Chart::HEIGHT %>" role="img"
|
|
13
|
+
aria-label="Runs per day and average execution time of <%= h(@job) %>">
|
|
14
|
+
<% @chart.ticks.each do |tick| -%>
|
|
15
|
+
<line class="chart-grid" x1="<%= Cosmo::Web::Chart::LEFT %>" x2="<%= Cosmo::Web::Chart::RIGHT %>"
|
|
16
|
+
y1="<%= tick[:y] %>" y2="<%= tick[:y] %>"/>
|
|
17
|
+
<text class="chart-axis" x="<%= Cosmo::Web::Chart::LEFT - 6 %>" y="<%= tick[:y] + 4 %>"
|
|
18
|
+
text-anchor="end"><%= format_numbers(tick[:count]) %></text>
|
|
19
|
+
<text class="chart-axis chart-axis-exec" x="<%= Cosmo::Web::Chart::RIGHT + 6 %>" y="<%= tick[:y] + 4 %>"
|
|
20
|
+
text-anchor="start"><%= format_numbers(tick[:exec_ms]) %></text>
|
|
21
|
+
<% end -%>
|
|
22
|
+
<% @chart.bars.each do |bar| -%>
|
|
23
|
+
<rect class="chart-bar" x="<%= bar[:x] %>" y="<%= bar[:y] %>" width="<%= bar[:width] %>"
|
|
24
|
+
height="<%= bar[:height] %>"><title><%= h(bar[:title]) %></title></rect>
|
|
25
|
+
<% end -%>
|
|
26
|
+
<polyline class="chart-line" points="<%= @chart.line %>"/>
|
|
27
|
+
<% @chart.points.each do |point| -%>
|
|
28
|
+
<circle class="chart-point" cx="<%= point[:x] %>" cy="<%= point[:y] %>" r="4"><title><%= h(point[:title]) %></title></circle>
|
|
29
|
+
<% end -%>
|
|
30
|
+
<% @chart.labels.each do |label| -%>
|
|
31
|
+
<text class="chart-axis" x="<%= label[:x] %>" y="<%= Cosmo::Web::Chart::BOTTOM + 18 %>"
|
|
32
|
+
text-anchor="middle"><%= label[:text] %></text>
|
|
33
|
+
<% end -%>
|
|
34
|
+
</svg>
|
|
35
|
+
<% end -%>
|
|
36
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<div id="metrics-panel"
|
|
2
|
+
hx-get="<%= url_for('/metrics/_panel', { days: @days, job: u(@job) }) %>"
|
|
3
|
+
hx-trigger="every 30s"
|
|
4
|
+
hx-swap="outerHTML">
|
|
5
|
+
<% if @rows.empty? -%>
|
|
6
|
+
<div class="alert alert-success">No metrics recorded in this period.</div>
|
|
7
|
+
<% else -%>
|
|
8
|
+
<% if @chart -%>
|
|
9
|
+
<%= render('metrics/_chart', { job: @job, days: @days, chart: @chart }) %>
|
|
10
|
+
<% end -%>
|
|
11
|
+
<%= render('metrics/_table', { rows: @rows, job: @job, days: @days }) %>
|
|
12
|
+
<% end -%>
|
|
13
|
+
</div>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<% selectable = @days > 1 -%>
|
|
2
|
+
<% if selectable -%>
|
|
3
|
+
<p class="metrics-hint">Click a job to chart it.</p>
|
|
4
|
+
<% end -%>
|
|
5
|
+
<div class="table-container">
|
|
6
|
+
<table class="metrics-table">
|
|
7
|
+
<thead>
|
|
8
|
+
<tr>
|
|
9
|
+
<th>Job</th>
|
|
10
|
+
<th>Runs</th>
|
|
11
|
+
<th>Failed</th>
|
|
12
|
+
<th>Avg execution</th>
|
|
13
|
+
<th>Avg wait</th>
|
|
14
|
+
</tr>
|
|
15
|
+
</thead>
|
|
16
|
+
<tbody>
|
|
17
|
+
<% @rows.each do |row| -%>
|
|
18
|
+
<% selected = selectable && row[:job] == @job -%>
|
|
19
|
+
<tr<% if selectable -%>
|
|
20
|
+
class="selectable<%= ' selected' if selected %>"
|
|
21
|
+
hx-get="<%= url_for('/metrics/_panel', { days: @days, job: u(row[:job]) }) %>"
|
|
22
|
+
hx-target="#metrics-panel"
|
|
23
|
+
hx-swap="outerHTML"
|
|
24
|
+
<% if selected -%>aria-selected="true"<% end -%>
|
|
25
|
+
<% end -%>>
|
|
26
|
+
<td><code><%= h(row[:job]) %></code></td>
|
|
27
|
+
<td><%= format_numbers(row[:count]) %></td>
|
|
28
|
+
<td>
|
|
29
|
+
<% if row[:failed].positive? -%>
|
|
30
|
+
<span class="badge badge-danger"><%= format_numbers(row[:failed]) %></span>
|
|
31
|
+
<% else -%>
|
|
32
|
+
<span class="text-muted">0</span>
|
|
33
|
+
<% end -%>
|
|
34
|
+
</td>
|
|
35
|
+
<td><%= duration(row[:exec_ms]) %></td>
|
|
36
|
+
<td><%= duration(row[:wait_ms]) %></td>
|
|
37
|
+
</tr>
|
|
38
|
+
<% end -%>
|
|
39
|
+
</tbody>
|
|
40
|
+
</table>
|
|
41
|
+
</div>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<section>
|
|
2
|
+
<header><%= render('jobs/_tabs') %></header>
|
|
3
|
+
|
|
4
|
+
<div class="nav">
|
|
5
|
+
<% @ranges.each do |range, name| -%>
|
|
6
|
+
<a href="<%= url_for('/metrics', { days: range }) %>"
|
|
7
|
+
class="btn <%= 'btn-primary' if @days == range %>"><%= h(name) %></a>
|
|
8
|
+
<% end -%>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<div id="metrics-panel"
|
|
12
|
+
hx-get="<%= url_for('/metrics/_panel', { days: @days }) %>"
|
|
13
|
+
hx-trigger="load"
|
|
14
|
+
hx-swap="outerHTML">
|
|
15
|
+
<div class="alert alert-info">Loading metrics…</div>
|
|
16
|
+
</div>
|
|
17
|
+
</section>
|
data/lib/cosmo/web.rb
CHANGED
|
@@ -11,6 +11,7 @@ require "cosmo/web/controllers/streams"
|
|
|
11
11
|
require "cosmo/web/controllers/actions"
|
|
12
12
|
require "cosmo/web/controllers/crons"
|
|
13
13
|
require "cosmo/web/controllers/batches"
|
|
14
|
+
require "cosmo/web/controllers/metrics"
|
|
14
15
|
|
|
15
16
|
module Cosmo
|
|
16
17
|
class Web
|
|
@@ -51,6 +52,8 @@ module Cosmo
|
|
|
51
52
|
in [:get, "/crons/_table"] then [Controllers::Crons, :_table]
|
|
52
53
|
in [:delete, "/crons/delete"] then [Controllers::Crons, :delete]
|
|
53
54
|
in [:post, "/crons/run"] then [Controllers::Crons, :run_now]
|
|
55
|
+
in [:get, "/metrics"] then [Controllers::Metrics, :index]
|
|
56
|
+
in [:get, "/metrics/_panel"] then [Controllers::Metrics, :_panel]
|
|
54
57
|
in [:get, "/batches"] then [Controllers::Batches, :index]
|
|
55
58
|
in [:get, "/batches/_table"] then [Controllers::Batches, :_table]
|
|
56
59
|
in [:get, "/actions"] then [Controllers::Actions, :index]
|
data/lib/cosmo.rb
CHANGED
|
@@ -14,6 +14,8 @@ require "cosmo/cli"
|
|
|
14
14
|
require "cosmo/heartbeat"
|
|
15
15
|
require "cosmo/engine"
|
|
16
16
|
require "cosmo/api"
|
|
17
|
+
require "cosmo/middleware"
|
|
18
|
+
require "cosmo/services"
|
|
17
19
|
require "cosmo/railtie" if defined?(Rails::Railtie)
|
|
18
20
|
|
|
19
21
|
module Cosmo
|
|
@@ -22,18 +24,54 @@ module Cosmo
|
|
|
22
24
|
# Optional, requires rack and webrick gems. Loaded only when an HTTP port is configured.
|
|
23
25
|
autoload :HTTPServer, "cosmo/http_server"
|
|
24
26
|
|
|
27
|
+
# Programmatic setup, typically from an initializer:
|
|
28
|
+
#
|
|
29
|
+
# Cosmo.configure do |config|
|
|
30
|
+
# config.logger = Rails.logger
|
|
31
|
+
# config.log_level = :debug
|
|
32
|
+
# config.server_middleware { |c| c.add MyMiddleware }
|
|
33
|
+
# end
|
|
34
|
+
#
|
|
35
|
+
# @yieldparam config [Config] the {Config} singleton
|
|
36
|
+
# @return [void]
|
|
37
|
+
def self.configure
|
|
38
|
+
yield Config.instance
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Hands +error+ to every handler in {Config#error_handlers}. A handler that raises is logged and skipped, so error
|
|
42
|
+
# reporting never breaks processing.
|
|
43
|
+
#
|
|
44
|
+
# @param error [Exception]
|
|
45
|
+
# @param context [Hash] where it happened, see {Config#error_handlers}
|
|
46
|
+
# @return [void]
|
|
47
|
+
def self.handle_error(error, context)
|
|
48
|
+
Config.error_handlers.each do |handler|
|
|
49
|
+
handler.call(error, context)
|
|
50
|
+
rescue StandardError => e
|
|
51
|
+
Logger.error "Error handler failed: #{e.class}: #{e.message}"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
25
55
|
class Error < StandardError; end
|
|
26
56
|
|
|
27
57
|
class ArgumentError < Error; end
|
|
28
58
|
|
|
29
59
|
class NotImplementedError < Error; end
|
|
30
60
|
|
|
61
|
+
class ConfigError < Error; end
|
|
62
|
+
|
|
31
63
|
class ConfigNotFoundError < Error
|
|
32
64
|
def initialize(config_file)
|
|
33
65
|
super("No such file #{config_file}")
|
|
34
66
|
end
|
|
35
67
|
end
|
|
36
68
|
|
|
69
|
+
class SchedulingDisabledError < Error
|
|
70
|
+
def initialize
|
|
71
|
+
super("Scheduling is turned off (config.scheduled.enabled = false), so delayed jobs and crons are unavailable")
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
37
75
|
class StreamNotFoundError < Error
|
|
38
76
|
def initialize(stream_name)
|
|
39
77
|
super("Missing stream `#{stream_name}`")
|
data/sig/cosmo/api/counter.rbs
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
module Cosmo
|
|
2
2
|
module API
|
|
3
3
|
class Counter
|
|
4
|
-
STREAM_NAME: ::String
|
|
5
|
-
STREAM_CONFIG: Hash[Symbol, untyped]
|
|
6
|
-
|
|
7
4
|
@namespace: ::String
|
|
8
5
|
|
|
9
6
|
def self.setup!: () -> void
|
|
10
7
|
|
|
8
|
+
def self.stream_config: () -> Hash[Symbol, untyped]
|
|
9
|
+
|
|
11
10
|
def initialize: (::String namespace) -> void
|
|
12
11
|
|
|
13
12
|
def increment: (Symbol key, ?by: ::Integer) -> ::Integer
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Cosmo
|
|
2
|
+
module API
|
|
3
|
+
module Stats
|
|
4
|
+
class Metrics < Counter
|
|
5
|
+
STREAM_NAME: ::String
|
|
6
|
+
DESCRIPTION: ::String
|
|
7
|
+
DAY: Integer
|
|
8
|
+
|
|
9
|
+
self.@instance: Metrics
|
|
10
|
+
|
|
11
|
+
@lock: Thread::Mutex
|
|
12
|
+
@buffer: Hash[[::String, ::String], Hash[Symbol, Numeric]]
|
|
13
|
+
|
|
14
|
+
def self.instance: () -> Metrics
|
|
15
|
+
|
|
16
|
+
def self.stream_config: () -> Hash[Symbol, untyped]
|
|
17
|
+
|
|
18
|
+
def initialize: () -> void
|
|
19
|
+
|
|
20
|
+
def record: (::String job_class, ?exec_ms: Numeric?, ?wait_ms: Numeric?, ?failed: bool) -> void
|
|
21
|
+
|
|
22
|
+
def flush: () -> void
|
|
23
|
+
|
|
24
|
+
def summary: (?days: Integer) -> Array[Hash[Symbol, untyped]]
|
|
25
|
+
|
|
26
|
+
def daily: (::String job_class, ?days: Integer) -> Array[Hash[Symbol, untyped]]
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def buffer: () -> Hash[[::String, ::String], Hash[Symbol, Numeric]]
|
|
31
|
+
|
|
32
|
+
def read: (Integer days) -> Array[[[::String, ::String, Symbol], Integer]]
|
|
33
|
+
|
|
34
|
+
def dates: (Integer days) -> Array[::String]
|
|
35
|
+
|
|
36
|
+
def row: (Hash[Symbol, Numeric] sums) -> Hash[Symbol, untyped]
|
|
37
|
+
|
|
38
|
+
def average: (Numeric total, Numeric count) -> Float?
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -2,12 +2,13 @@ module Cosmo
|
|
|
2
2
|
module API
|
|
3
3
|
module Stats
|
|
4
4
|
class Totals < Counter
|
|
5
|
+
STREAM_NAME: ::String
|
|
6
|
+
DESCRIPTION: ::String
|
|
7
|
+
|
|
5
8
|
self.@instance: Totals
|
|
6
9
|
|
|
7
10
|
def self.instance: () -> Totals
|
|
8
11
|
|
|
9
|
-
def with: () { () -> bool } -> void
|
|
10
|
-
|
|
11
12
|
def processed: () -> ::Integer
|
|
12
13
|
|
|
13
14
|
def failed: () -> ::Integer
|
data/sig/cosmo/batch.rbs
CHANGED
|
@@ -3,17 +3,16 @@ module Cosmo
|
|
|
3
3
|
extend Dispatcher
|
|
4
4
|
|
|
5
5
|
BUCKET: ::String
|
|
6
|
-
DEFAULT_EXPIRY: ::String
|
|
7
6
|
EVENTS: ::Array[Symbol]
|
|
8
7
|
|
|
9
|
-
self.@counter:
|
|
8
|
+
self.@counter: Batch::Counters
|
|
10
9
|
self.@kv: API::KV
|
|
11
10
|
|
|
12
11
|
def self.current: () -> Batch?
|
|
13
12
|
|
|
14
13
|
def self.notify: (::String bid, ::String jid, success: bool) -> void
|
|
15
14
|
|
|
16
|
-
def self.counter: () ->
|
|
15
|
+
def self.counter: () -> Batch::Counters
|
|
17
16
|
|
|
18
17
|
def self.kv: () -> API::KV
|
|
19
18
|
|
|
@@ -36,7 +35,7 @@ module Cosmo
|
|
|
36
35
|
|
|
37
36
|
private
|
|
38
37
|
|
|
39
|
-
def counter: () ->
|
|
38
|
+
def counter: () -> Batch::Counters
|
|
40
39
|
|
|
41
40
|
def kv: () -> API::KV
|
|
42
41
|
|
data/sig/cosmo/cli.rbs
CHANGED
data/sig/cosmo/client.rbs
CHANGED
|
@@ -54,6 +54,8 @@ module Cosmo
|
|
|
54
54
|
|
|
55
55
|
def get_message: (::String | Symbol name, **untyped options) -> NATS::JetStream::API::RawStreamMsg
|
|
56
56
|
|
|
57
|
+
def last_messages: (::String stream_name, Array[::String] filters) -> Hash[::String, ::String]
|
|
58
|
+
|
|
57
59
|
def delete_message: (::String name, ::Integer seq) -> Hash[::String, untyped]
|
|
58
60
|
|
|
59
61
|
def purge: (::String stream_name, ::String? subject) -> ::Integer?
|
|
@@ -66,6 +68,6 @@ module Cosmo
|
|
|
66
68
|
|
|
67
69
|
def ephemeral_subscribe: (::String | Array[::String] subject, Hash[Symbol, untyped] config) -> untyped
|
|
68
70
|
|
|
69
|
-
def create_kv_with_msg_ttl: (::String name, **untyped opts) -> untyped
|
|
71
|
+
def create_kv_with_msg_ttl: (::String name, ?replicas: Integer, **untyped opts) -> untyped
|
|
70
72
|
end
|
|
71
73
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Cosmo
|
|
2
|
+
class Config < ::Hash[Symbol, untyped]
|
|
3
|
+
class Scheduled
|
|
4
|
+
attr_accessor enabled: bool
|
|
5
|
+
|
|
6
|
+
def self.new: (enabled: bool) -> Scheduled
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class Dead
|
|
10
|
+
attr_accessor enabled: bool
|
|
11
|
+
attr_accessor max_age: Numeric | ::String
|
|
12
|
+
attr_accessor max_msgs: Integer
|
|
13
|
+
attr_accessor max_bytes: Integer
|
|
14
|
+
|
|
15
|
+
def self.new: (enabled: bool, max_age: Numeric | ::String, max_msgs: Integer, max_bytes: Integer) -> Dead
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Batches
|
|
19
|
+
attr_accessor expiry: Numeric | ::String
|
|
20
|
+
|
|
21
|
+
def self.new: (expiry: Numeric | ::String) -> Batches
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class Metrics
|
|
25
|
+
attr_accessor enabled: bool
|
|
26
|
+
attr_accessor retention: Numeric | ::String
|
|
27
|
+
|
|
28
|
+
def self.new: (enabled: bool, retention: Numeric | ::String) -> Metrics
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/sig/cosmo/config.rbs
CHANGED
|
@@ -2,6 +2,8 @@ module Cosmo
|
|
|
2
2
|
class Config < ::Hash[Symbol, untyped]
|
|
3
3
|
NANO: Integer
|
|
4
4
|
DEFAULT_PATH: ::String
|
|
5
|
+
DEFAULTS_FILE: ::String
|
|
6
|
+
SERVICE_STREAMS: Array[Symbol]
|
|
5
7
|
|
|
6
8
|
self.@instance: Config
|
|
7
9
|
self.@internal: Hash[Symbol, untyped]
|
|
@@ -12,8 +14,14 @@ module Cosmo
|
|
|
12
14
|
|
|
13
15
|
def self.to_ns: (Numeric seconds) -> ::Integer
|
|
14
16
|
|
|
17
|
+
def self.read: (::String path) -> Hash[Symbol, untyped]
|
|
18
|
+
|
|
15
19
|
def self.parse_file: (::String path) -> Hash[Symbol, untyped]
|
|
16
20
|
|
|
21
|
+
def self.build: (Hash[Symbol, untyped] user) -> Hash[Symbol, untyped]
|
|
22
|
+
|
|
23
|
+
def self.validate!: (Hash[Symbol, untyped] config) -> void
|
|
24
|
+
|
|
17
25
|
def self.normalize!: (Hash[Symbol, untyped] config) -> void
|
|
18
26
|
|
|
19
27
|
def self.deliver_policy: (Symbol | ::String | Time start_position) -> Hash[Symbol, ::String]
|
|
@@ -24,15 +32,61 @@ module Cosmo
|
|
|
24
32
|
|
|
25
33
|
def self.dig: (*Symbol keys) -> untyped
|
|
26
34
|
|
|
27
|
-
def self.
|
|
35
|
+
def self.load: (?::String? path, ?overrides: Hash[Symbol, untyped]?) -> void
|
|
36
|
+
|
|
37
|
+
def self.replicas: () -> Integer
|
|
38
|
+
|
|
39
|
+
def self.scheduled: () -> Scheduled
|
|
40
|
+
|
|
41
|
+
def self.dead: () -> Dead
|
|
42
|
+
|
|
43
|
+
def self.batches: () -> Batches
|
|
44
|
+
|
|
45
|
+
def self.metrics: () -> Metrics
|
|
46
|
+
|
|
47
|
+
def self.server_middleware: () ?{ (Middleware::Chain chain) -> void } -> Middleware::Chain
|
|
48
|
+
|
|
49
|
+
def self.client_middleware: () ?{ (Middleware::Chain chain) -> void } -> Middleware::Chain
|
|
50
|
+
|
|
51
|
+
def self.error_handlers: () -> Array[untyped]
|
|
52
|
+
|
|
53
|
+
@server_middleware: Middleware::Chain
|
|
54
|
+
|
|
55
|
+
def load: (?::String? path, ?overrides: Hash[Symbol, untyped]?) -> void
|
|
56
|
+
|
|
57
|
+
def server_middleware: () ?{ (Middleware::Chain chain) -> void } -> Middleware::Chain
|
|
58
|
+
|
|
59
|
+
@client_middleware: Middleware::Chain
|
|
60
|
+
|
|
61
|
+
def client_middleware: () ?{ (Middleware::Chain chain) -> void } -> Middleware::Chain
|
|
62
|
+
|
|
63
|
+
@error_handlers: Array[untyped]
|
|
64
|
+
|
|
65
|
+
def error_handlers: () -> Array[untyped]
|
|
66
|
+
|
|
67
|
+
@replicas: Integer?
|
|
68
|
+
@scheduled: Scheduled
|
|
69
|
+
@dead: Dead
|
|
70
|
+
@batches: Batches
|
|
71
|
+
|
|
72
|
+
attr_writer replicas: Integer
|
|
73
|
+
|
|
74
|
+
def replicas: () -> Integer
|
|
75
|
+
|
|
76
|
+
def scheduled: () -> Scheduled
|
|
77
|
+
|
|
78
|
+
def dead: () -> Dead
|
|
79
|
+
|
|
80
|
+
def batches: () -> Batches
|
|
28
81
|
|
|
29
|
-
|
|
82
|
+
@metrics: Metrics
|
|
30
83
|
|
|
31
|
-
def
|
|
84
|
+
def metrics: () -> Metrics
|
|
32
85
|
|
|
86
|
+
def logger: () -> untyped
|
|
33
87
|
|
|
34
|
-
def
|
|
88
|
+
def logger=: (untyped logger) -> void
|
|
35
89
|
|
|
36
|
-
def
|
|
90
|
+
def log_level=: (Symbol | ::String | Integer level) -> void
|
|
37
91
|
end
|
|
38
92
|
end
|