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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +199 -103
  3. data/lib/cosmo/active_job/adapter.rb +1 -2
  4. data/lib/cosmo/api/counter.rb +16 -16
  5. data/lib/cosmo/api/cron.rb +3 -0
  6. data/lib/cosmo/api/registry.rb +1 -1
  7. data/lib/cosmo/api/stats/busy.rb +1 -1
  8. data/lib/cosmo/api/stats/metrics.rb +109 -0
  9. data/lib/cosmo/api/stats/processes.rb +14 -4
  10. data/lib/cosmo/api/stats/totals.rb +4 -12
  11. data/lib/cosmo/api/stats.rb +3 -2
  12. data/lib/cosmo/batch/counters.rb +19 -0
  13. data/lib/cosmo/batch.rb +6 -6
  14. data/lib/cosmo/cli.rb +21 -5
  15. data/lib/cosmo/client.rb +28 -2
  16. data/lib/cosmo/config/cosmo.yml +58 -0
  17. data/lib/cosmo/config/settings.rb +20 -0
  18. data/lib/cosmo/config.rb +144 -19
  19. data/lib/cosmo/heartbeat.rb +11 -0
  20. data/lib/cosmo/job/data.rb +17 -12
  21. data/lib/cosmo/job/enqueuer.rb +53 -0
  22. data/lib/cosmo/job/limit.rb +2 -2
  23. data/lib/cosmo/job/processor.rb +27 -52
  24. data/lib/cosmo/job/requeue.rb +20 -0
  25. data/lib/cosmo/job.rb +8 -22
  26. data/lib/cosmo/logger.rb +15 -2
  27. data/lib/cosmo/middleware/busy.rb +15 -0
  28. data/lib/cosmo/middleware/chain.rb +137 -0
  29. data/lib/cosmo/middleware/limit.rb +50 -0
  30. data/lib/cosmo/middleware/metrics.rb +39 -0
  31. data/lib/cosmo/{sentry/job_processor_middleware.rb → middleware/sentry.rb} +26 -30
  32. data/lib/cosmo/middleware/totals.rb +29 -0
  33. data/lib/cosmo/middleware.rb +7 -0
  34. data/lib/cosmo/processor.rb +11 -5
  35. data/lib/cosmo/publisher.rb +1 -13
  36. data/lib/cosmo/railtie.rb +2 -2
  37. data/lib/cosmo/services.rb +69 -0
  38. data/lib/cosmo/stream/processor.rb +7 -1
  39. data/lib/cosmo/utils/hash.rb +7 -8
  40. data/lib/cosmo/version.rb +1 -1
  41. data/lib/cosmo/web/assets/app.css +58 -0
  42. data/lib/cosmo/web/chart.rb +98 -0
  43. data/lib/cosmo/web/controllers/crons.rb +8 -0
  44. data/lib/cosmo/web/controllers/jobs.rb +16 -2
  45. data/lib/cosmo/web/controllers/metrics.rb +48 -0
  46. data/lib/cosmo/web/helpers/application.rb +10 -0
  47. data/lib/cosmo/web/views/batches/_table.erb +11 -0
  48. data/lib/cosmo/web/views/jobs/_processes.erb +49 -35
  49. data/lib/cosmo/web/views/jobs/_stats.erb +4 -0
  50. data/lib/cosmo/web/views/jobs/_tabs.erb +6 -0
  51. data/lib/cosmo/web/views/jobs/index.erb +1 -1
  52. data/lib/cosmo/web/views/layout.erb +1 -1
  53. data/lib/cosmo/web/views/metrics/_chart.erb +36 -0
  54. data/lib/cosmo/web/views/metrics/_panel.erb +13 -0
  55. data/lib/cosmo/web/views/metrics/_table.erb +41 -0
  56. data/lib/cosmo/web/views/metrics/index.erb +17 -0
  57. data/lib/cosmo/web.rb +3 -0
  58. data/lib/cosmo.rb +38 -0
  59. data/sig/cosmo/api/counter.rbs +2 -3
  60. data/sig/cosmo/api/stats/busy.rbs +1 -1
  61. data/sig/cosmo/api/stats/metrics.rbs +42 -0
  62. data/sig/cosmo/api/stats/processes.rbs +3 -0
  63. data/sig/cosmo/api/stats/totals.rbs +3 -2
  64. data/sig/cosmo/batch/counters.rbs +10 -0
  65. data/sig/cosmo/batch.rbs +3 -4
  66. data/sig/cosmo/cli.rbs +2 -0
  67. data/sig/cosmo/client.rbs +3 -1
  68. data/sig/cosmo/config/settings.rbs +31 -0
  69. data/sig/cosmo/config.rbs +59 -5
  70. data/sig/cosmo/heartbeat.rbs +2 -0
  71. data/sig/cosmo/job/data.rbs +5 -3
  72. data/sig/cosmo/job/enqueuer.rbs +11 -0
  73. data/sig/cosmo/job/processor.rbs +3 -5
  74. data/sig/cosmo/job/requeue.rbs +9 -0
  75. data/sig/cosmo/job.rbs +0 -2
  76. data/sig/cosmo/logger.rbs +6 -0
  77. data/sig/cosmo/middleware/chain.rbs +51 -0
  78. data/sig/cosmo/middleware/sentry.rbs +14 -0
  79. data/sig/cosmo/middleware.rbs +35 -0
  80. data/sig/cosmo/processor.rbs +2 -0
  81. data/sig/cosmo/publisher.rbs +1 -9
  82. data/sig/cosmo/services.rbs +18 -0
  83. data/sig/cosmo/stream/processor.rbs +2 -0
  84. data/sig/cosmo/utils/hash.rbs +2 -3
  85. data/sig/cosmo.rbs +11 -0
  86. metadata +30 -3
  87. 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 @processes.empty? -%>
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
- <% @processes.each do |process| -%>
51
- <% nats = Hash(process[:nats]) -%>
52
- <tr>
53
- <td>
54
- <div class="job-class"><%= h(process[:hostname]) %>:<%= h(process[:pid]) %></div>
55
- <div class="text-muted" style="font-size: var(--font-size-small);">
56
- <%= h(nats[:ip] || "unknown IP") %>
57
- <% if nats[:name] -%>&middot; <code><%= h(nats[:name]) %></code><% end -%>
58
- </div>
59
- </td>
60
- <td>
61
- <% cmdline = process[:cmdline].to_s -%>
62
- <div class="job-id">
63
- <details>
64
- <summary>
65
- <code><%= h(cmdline.length > 40 ? "#{cmdline[0, 40]}…" : cmdline) %></code>
66
- </summary>
67
- <code style="font-size: var(--font-size-small);"><%= h(cmdline) %></code>
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] -%>&middot; <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]) %> &middot; <%= h(process[:ruby]) %>
70
80
  <% if process[:http_port] -%>&middot; http :<%= h(process[:http_port]) %><% end -%>
71
81
  </div>
72
- </details>
73
- </div>
74
- </td>
75
- <td>
76
- <% Hash(process[:subscriptions]).each do |type, names| -%>
77
- <div><span class="text-muted"><%= h(type) %>:</span> <code><%= h(Array(names).join(", ")) %></code></div>
78
- <% end -%>
79
- </td>
80
- <td><%= h(process[:busy]) %> / <%= h(process[:concurrency]) %></td>
81
- <td><span class="badge <%= states.fetch(process[:state].to_s, 'badge-info') %>"><%= h(process[:state]) %></span></td>
82
- <td><%= nats[:rtt] ? "#{h(nats[:rtt])}ms" : '&mdash;' %></td>
83
- <td><%= h(nats[:reconnects].to_i) %></td>
84
- <td><%= process[:rss] ? format_bytes(process[:rss].to_i * 1024) : '&mdash;' %></td>
85
- <td><%= elapsed(process[:started_at]) %></td>
86
- <td><%= elapsed(process[:beat_at]) %> ago</td>
87
- </tr>
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" : '&mdash;' %></td>
96
+ <td><%= h(nats[:reconnects].to_i) %></td>
97
+ <td><%= process[:rss] ? format_bytes(process[:rss].to_i * 1024) : '&mdash;' %></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') %>">&#x1F4EC; 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') %>">&#x23F0; 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') %>">&#x1F480; 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') %>">&#x1F4CB; Dashboard</a>
4
4
  <a href="<%= url_for('/batches') %>"
5
5
  class="<%= 'active' if path_prefix?('/batches') %>">&#x1F4E6; Batches</a>
6
+ <% if Cosmo::Config.scheduled.enabled -%>
6
7
  <a href="<%= url_for('/crons') %>"
7
8
  class="<%= 'active' if path_prefix?('/crons') %>">&#x23F0; Crons</a>
9
+ <% end -%>
10
+ <% if Cosmo::Config.metrics.enabled -%>
11
+ <a href="<%= url_for('/metrics') %>"
12
+ class="<%= 'active' if path_prefix?('/metrics') %>">&#x1F4CA; 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&hellip;</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&hellip;</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}`")
@@ -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
@@ -12,7 +12,7 @@ module Cosmo
12
12
 
13
13
  def initialize: () -> void
14
14
 
15
- def with: (untyped message) { () -> void } -> void
15
+ def with: [T] (untyped message) { () -> T } -> T
16
16
 
17
17
  def add: (untyped message) -> void
18
18
 
@@ -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
@@ -4,6 +4,9 @@ module Cosmo
4
4
  class Processes < Registry
5
5
  TTL: ::Integer
6
6
  BUCKET: ::String
7
+ KINDS: Array[::String]
8
+
9
+ def self.kind: (Hash[Symbol, untyped] process) -> ::String
7
10
 
8
11
  def list: (?page: ::Integer?, ?limit: ::Integer) -> Array[Hash[Symbol, untyped]]
9
12
 
@@ -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
@@ -0,0 +1,10 @@
1
+ module Cosmo
2
+ class Batch
3
+ class Counters < API::Counter
4
+ STREAM_NAME: ::String
5
+ DESCRIPTION: ::String
6
+
7
+ def self.stream_config: () -> Hash[Symbol, untyped]
8
+ end
9
+ end
10
+ end
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: API::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: () -> API::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: () -> API::Counter
38
+ def counter: () -> Batch::Counters
40
39
 
41
40
  def kv: () -> API::KV
42
41
 
data/sig/cosmo/cli.rbs CHANGED
@@ -20,6 +20,8 @@ module Cosmo
20
20
 
21
21
  def load_config: (Hash[Symbol, untyped] flags) -> void
22
22
 
23
+ def run_init: () -> void
24
+
23
25
  def boot_application: () -> void
24
26
 
25
27
  def require_path: (::String? path) -> void
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.to_h: () -> Hash[Symbol, untyped]
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
- def self.set: (*untyped) -> untyped
82
+ @metrics: Metrics
30
83
 
31
- def self.load: (?::String? path) -> void
84
+ def metrics: () -> Metrics
32
85
 
86
+ def logger: () -> untyped
33
87
 
34
- def set: (*untyped) -> untyped
88
+ def logger=: (untyped logger) -> void
35
89
 
36
- def load: (?::String? path) -> void
90
+ def log_level=: (Symbol | ::String | Integer level) -> void
37
91
  end
38
92
  end