cosmonats 0.4.2 → 0.5.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +77 -5
  3. data/lib/cosmo/active_job/executor.rb +9 -0
  4. data/lib/cosmo/active_job/options.rb +8 -5
  5. data/lib/cosmo/api/batch.rb +84 -0
  6. data/lib/cosmo/api/counter.rb +17 -6
  7. data/lib/cosmo/api/cron/entry.rb +1 -1
  8. data/lib/cosmo/api/cron.rb +1 -1
  9. data/lib/cosmo/api/kv.rb +7 -0
  10. data/lib/cosmo/api.rb +1 -0
  11. data/lib/cosmo/batch/callback.rb +17 -0
  12. data/lib/cosmo/batch/dispatcher.rb +86 -0
  13. data/lib/cosmo/batch.rb +164 -0
  14. data/lib/cosmo/cli.rb +31 -25
  15. data/lib/cosmo/config.rb +10 -2
  16. data/lib/cosmo/job/data.rb +14 -8
  17. data/lib/cosmo/job/processor.rb +78 -18
  18. data/lib/cosmo/job.rb +46 -9
  19. data/lib/cosmo/processor.rb +7 -7
  20. data/lib/cosmo/utils/duration.rb +33 -0
  21. data/lib/cosmo/utils.rb +1 -0
  22. data/lib/cosmo/version.rb +1 -1
  23. data/lib/cosmo/web/assets/app.css +9 -1
  24. data/lib/cosmo/web/controllers/batches.rb +21 -0
  25. data/lib/cosmo/web/controllers/jobs.rb +7 -4
  26. data/lib/cosmo/web/views/batches/_table.erb +47 -0
  27. data/lib/cosmo/web/views/batches/index.erb +10 -0
  28. data/lib/cosmo/web/views/jobs/_enqueued.erb +37 -35
  29. data/lib/cosmo/web/views/jobs/_tabs.erb +2 -0
  30. data/lib/cosmo/web/views/layout.erb +1 -1
  31. data/lib/cosmo/web.rb +3 -0
  32. data/lib/cosmo.rb +1 -0
  33. data/sig/cosmo/active_job/executor.rbs +2 -0
  34. data/sig/cosmo/api/batch.rbs +43 -0
  35. data/sig/cosmo/batch/callback.rbs +9 -0
  36. data/sig/cosmo/batch/dispatcher.rbs +23 -0
  37. data/sig/cosmo/batch.rbs +49 -0
  38. data/sig/cosmo/config.rbs +2 -0
  39. data/sig/cosmo/job/data.rbs +3 -1
  40. data/sig/cosmo/job/processor.rbs +17 -1
  41. data/sig/cosmo/job.rbs +10 -0
  42. data/sig/cosmo/processor.rbs +3 -3
  43. data/sig/cosmo/utils/duration.rbs +11 -0
  44. metadata +28 -1
@@ -14,10 +14,12 @@
14
14
 
15
15
  <div class="pending">
16
16
  <span></span>
17
- <span class="text-muted"><%= @total %> message(s) pending</span>
17
+ <span class="text-muted"><%= @total.to_i %> message(s) pending</span>
18
18
  </div>
19
19
 
20
- <% if @jobs.empty? -%>
20
+ <% if @jobs.nil? -%>
21
+ <div class="alert alert-success">No streams found</div>
22
+ <% elsif @jobs.empty? -%>
21
23
  <div class="alert alert-success">Stream <code><%= h(@stream_name) %></code> is empty</div>
22
24
  <% else -%>
23
25
  <div class="table-container">
@@ -61,44 +63,44 @@
61
63
  </div>
62
64
  <% end -%>
63
65
 
64
- <% if @total_pages > 1 -%>
65
- <div class="pagination">
66
- <% if @page > 1 -%>
67
- <a hx-get="<%= url_for('/jobs/enqueued', stream_name: @stream_name, page: @page - 1, limit: @limit) %>"
68
- hx-target="#enqueued-poller"
69
- hx-swap="outerHTML"
70
- hx-push-url="true"
71
- hx-indicator="#global-spinner"
72
- class="btn">&#x2190; Prev</a>
73
- <% else -%>
74
- <span class="btn btn-disabled">&#x2190; Prev</span>
75
- <% end -%>
76
-
77
- <% pages(@page, @total_pages).each do |p| -%>
78
- <% if p == :gap -%>
79
- <span class="pagination-gap">&hellip;</span>
80
- <% elsif p == @page -%>
81
- <span class="btn btn-primary btn-disabled"><%= p %></span>
82
- <% else -%>
83
- <a hx-get="<%= url_for('/jobs/enqueued', stream_name: @stream_name, page: p, limit: @limit) %>"
66
+ <% if @total_pages.to_i > 1 -%>
67
+ <div class="pagination">
68
+ <% if @page > 1 -%>
69
+ <a hx-get="<%= url_for('/jobs/enqueued', stream_name: @stream_name, page: @page - 1, limit: @limit) %>"
84
70
  hx-target="#enqueued-poller"
85
71
  hx-swap="outerHTML"
86
72
  hx-push-url="true"
87
73
  hx-indicator="#global-spinner"
88
- class="btn"><%= p %></a>
74
+ class="btn">&#x2190; Prev</a>
75
+ <% else -%>
76
+ <span class="btn btn-disabled">&#x2190; Prev</span>
89
77
  <% end -%>
90
- <% end -%>
91
78
 
92
- <% if @page < @total_pages -%>
93
- <a hx-get="<%= url_for('/jobs/enqueued', stream_name: @stream_name, page: @page + 1, limit: @limit) %>"
94
- hx-target="#enqueued-poller"
95
- hx-swap="outerHTML"
96
- hx-push-url="true"
97
- hx-indicator="#global-spinner"
98
- class="btn">Next &#x2192;</a>
99
- <% else -%>
100
- <span class="btn btn-disabled">Next &#x2192;</span>
101
- <% end -%>
102
- </div>
79
+ <% pages(@page, @total_pages).each do |p| -%>
80
+ <% if p == :gap -%>
81
+ <span class="pagination-gap">&hellip;</span>
82
+ <% elsif p == @page -%>
83
+ <span class="btn btn-primary btn-disabled"><%= p %></span>
84
+ <% else -%>
85
+ <a hx-get="<%= url_for('/jobs/enqueued', stream_name: @stream_name, page: p, limit: @limit) %>"
86
+ hx-target="#enqueued-poller"
87
+ hx-swap="outerHTML"
88
+ hx-push-url="true"
89
+ hx-indicator="#global-spinner"
90
+ class="btn"><%= p %></a>
91
+ <% end -%>
92
+ <% end -%>
93
+
94
+ <% if @page < @total_pages -%>
95
+ <a hx-get="<%= url_for('/jobs/enqueued', stream_name: @stream_name, page: @page + 1, limit: @limit) %>"
96
+ hx-target="#enqueued-poller"
97
+ hx-swap="outerHTML"
98
+ hx-push-url="true"
99
+ hx-indicator="#global-spinner"
100
+ class="btn">Next &#x2192;</a>
101
+ <% else -%>
102
+ <span class="btn btn-disabled">Next &#x2192;</span>
103
+ <% end -%>
104
+ </div>
103
105
  <% end -%>
104
106
  </div>
@@ -1,6 +1,8 @@
1
1
  <nav class="tabs">
2
2
  <a href="<%= url_for('/jobs') %>"
3
3
  class="<%= 'active' if path_prefix?('/jobs') %>">&#x1F4CB; Dashboard</a>
4
+ <a href="<%= url_for('/batches') %>"
5
+ class="<%= 'active' if path_prefix?('/batches') %>">&#x1F4E6; Batches</a>
4
6
  <a href="<%= url_for('/crons') %>"
5
7
  class="<%= 'active' if path_prefix?('/crons') %>">&#x23F0; Crons</a>
6
8
  </nav>
@@ -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') %>">Jobs</a>
17
+ <a href="<%= url_for('/jobs') %>" class="<%= "active" if path_prefix?('/jobs', '/crons', '/batches') %>">Jobs</a>
18
18
  </li>
19
19
  <li>
20
20
  <a href="<%= url_for('/streams') %>" class="<%= "active" if current_page?('/streams') %>">Streams</a>
data/lib/cosmo/web.rb CHANGED
@@ -10,6 +10,7 @@ require "cosmo/web/controllers/jobs"
10
10
  require "cosmo/web/controllers/streams"
11
11
  require "cosmo/web/controllers/actions"
12
12
  require "cosmo/web/controllers/crons"
13
+ require "cosmo/web/controllers/batches"
13
14
 
14
15
  module Cosmo
15
16
  class Web
@@ -48,6 +49,8 @@ module Cosmo
48
49
  in [:get, "/crons/_table"] then [Controllers::Crons, :_table]
49
50
  in [:delete, "/crons/delete"] then [Controllers::Crons, :delete]
50
51
  in [:post, "/crons/run"] then [Controllers::Crons, :run_now]
52
+ in [:get, "/batches"] then [Controllers::Batches, :index]
53
+ in [:get, "/batches/_table"] then [Controllers::Batches, :_table]
51
54
  in [:get, "/actions"] then [Controllers::Actions, :index]
52
55
  in [:get, "/assets/htmx.min.js.gz"] then serve("htmx.2.0.8.min.js.gz",
53
56
  "application/javascript",
data/lib/cosmo.rb CHANGED
@@ -8,6 +8,7 @@ require "cosmo/version"
8
8
  require "cosmo/config"
9
9
  require "cosmo/logger"
10
10
  require "cosmo/job"
11
+ require "cosmo/batch"
11
12
  require "cosmo/stream"
12
13
  require "cosmo/cli"
13
14
  require "cosmo/engine"
@@ -3,6 +3,8 @@ module Cosmo
3
3
  class Executor
4
4
  include Cosmo::Job
5
5
 
6
+ def self.retry_in: (Hash[Symbol, untyped] data) -> Proc?
7
+
6
8
  def perform: (Hash[Symbol, untyped] job_data) -> void
7
9
  end
8
10
  end
@@ -0,0 +1,43 @@
1
+ module Cosmo
2
+ module API
3
+ class Batch
4
+ LIMIT: ::Integer
5
+
6
+ def self.all: (?limit: ::Integer) -> Array[Batch]
7
+
8
+ def self.kv: () -> KV
9
+
10
+ def self.counter: () -> Counter
11
+
12
+ @bid: ::String
13
+
14
+ attr_reader bid: ::String
15
+
16
+ def initialize: (::String bid) -> void
17
+
18
+ def meta: () -> Hash[Symbol, untyped]
19
+
20
+ def parent_id: () -> ::String?
21
+
22
+ def created_at: () -> ::Integer?
23
+
24
+ def ready?: () -> bool
25
+
26
+ def stats: () -> Hash[Symbol, untyped]
27
+
28
+ def callback: (Symbol event) -> Hash[Symbol, untyped]?
29
+
30
+ def callbacks: () -> Hash[Symbol, Hash[Symbol, untyped]?]
31
+
32
+ def fired?: (Symbol event) -> bool
33
+
34
+ private
35
+
36
+ def live_stats: () -> Hash[Symbol, untyped]
37
+
38
+ def kv: () -> KV
39
+
40
+ def counter: () -> Counter
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,9 @@
1
+ module Cosmo
2
+ class Batch
3
+ class Callback
4
+ include Cosmo::Job
5
+
6
+ def perform: (::String class_name, Symbol event, Hash[Symbol, untyped] status, Hash[Symbol, untyped] opts) -> void
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ module Cosmo
2
+ class Batch
3
+ module Dispatcher
4
+ private
5
+
6
+ def release_pending: (::String bid, ?msg_id: ::String?) -> void
7
+
8
+ def finalize: (::String bid) -> void
9
+
10
+ def purge_counters: (::String bid) -> void
11
+
12
+ def parent_propagate: (::String bid, failed: ::Integer) -> void
13
+
14
+ def link: (::String parent_id) -> void
15
+
16
+ def try_fire: (::String bid, Symbol event, ?stats: Hash[Symbol, untyped]?) -> void
17
+
18
+ def fireable?: (untyped callback_entry, Symbol event, Hash[Symbol, untyped]? stats) -> bool
19
+
20
+ def dispatch_callback: (untyped callback_entry, ::String bid, Symbol event, Hash[Symbol, untyped] stats) -> void
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,49 @@
1
+ module Cosmo
2
+ class Batch
3
+ extend Dispatcher
4
+
5
+ BUCKET: ::String
6
+ DEFAULT_EXPIRY: ::String
7
+ EVENTS: ::Array[Symbol]
8
+
9
+ self.@counter: API::Counter
10
+ self.@kv: API::KV
11
+
12
+ def self.current: () -> Batch?
13
+
14
+ def self.notify: (::String bid, ::String jid, success: bool) -> void
15
+
16
+ def self.counter: () -> API::Counter
17
+
18
+ def self.kv: () -> API::KV
19
+
20
+ @bid: ::String
21
+ @parent_id: ::String?
22
+
23
+ attr_reader bid: ::String
24
+
25
+ attr_reader parent_id: ::String?
26
+
27
+ def initialize: (?parent: ::String?) -> void
28
+
29
+ def jobs: () { () -> void } -> void
30
+
31
+ def on: (Symbol event, Class klass, **untyped) -> void
32
+
33
+ def register_job!: () -> void
34
+
35
+ def rollback_job!: () -> void
36
+
37
+ private
38
+
39
+ def counter: () -> API::Counter
40
+
41
+ def kv: () -> API::KV
42
+
43
+ def link: (::String parent_id) -> void
44
+
45
+ def release_pending: () -> void
46
+
47
+ def try_fire: (Symbol event) -> void
48
+ end
49
+ end
data/sig/cosmo/config.rbs CHANGED
@@ -10,6 +10,8 @@ module Cosmo
10
10
 
11
11
  def self.internal: () -> Hash[Symbol, untyped]
12
12
 
13
+ def self.to_ns: (Numeric seconds) -> ::Integer
14
+
13
15
  def self.parse_file: (::String path) -> Hash[Symbol, untyped]
14
16
 
15
17
  def self.normalize!: (Hash[Symbol, untyped] config) -> void
@@ -3,6 +3,8 @@ module Cosmo
3
3
  class Data
4
4
  DEFAULTS: { stream: Symbol, retry: Integer, dead: bool, limit: nil }
5
5
 
6
+ def self.default_retry: () -> Integer
7
+
6
8
  @class_name: ::String
7
9
  @args: Array[untyped]
8
10
  @options: Hash[Symbol, untyped]
@@ -27,7 +29,7 @@ module Cosmo
27
29
 
28
30
  def validate!: () -> void
29
31
 
30
- def retries: () -> (Integer | bool)
32
+ def retries: () -> Integer
31
33
 
32
34
  def dead: () -> bool
33
35
  end
@@ -1,6 +1,8 @@
1
1
  module Cosmo
2
2
  module Job
3
3
  class Processor < ::Cosmo::Processor
4
+ @weights: Array[Symbol]?
5
+
4
6
  private
5
7
 
6
8
  def setup: () -> void
@@ -17,7 +19,19 @@ module Cosmo
17
19
 
18
20
  def process: (Array[untyped] messages, untyped processor) -> void
19
21
 
20
- def handle_failure: (untyped message, Hash[Symbol, untyped] data) -> bool
22
+ def handle_failure: (untyped worker_class, untyped message, Hash[Symbol, untyped] data, Exception? exception) -> bool
23
+
24
+ def nak_message: (untyped worker_class, untyped message, Hash[Symbol, untyped] data, Integer current_attempt, Exception? exception) -> void
25
+
26
+ def warn_capped: (untyped message, Hash[Symbol, untyped] data, Integer capped_at) -> void
27
+
28
+ def deliver_cap: (untyped stream_name, Integer desired_retries) -> Integer?
29
+
30
+ def consumer_entry: (untyped stream_name) -> [untyped, Hash[Symbol, untyped], untyped]?
31
+
32
+ def retry_delay: (untyped worker_class, Hash[Symbol, untyped] data, Integer current_attempt, Exception? exception) -> Numeric
33
+
34
+ def default_retry_delay: (Integer current_attempt) -> Integer
21
35
 
22
36
  def subscribe: (Symbol stream_name, Hash[Symbol, untyped] config) -> [untyped, Hash[Symbol, untyped], nil]
23
37
 
@@ -28,6 +42,8 @@ module Cosmo
28
42
  def with_stats: (untyped message) { () -> untyped } -> void
29
43
 
30
44
  def acquire_concurrency_slot: (untyped worker_class, untyped message, Hash[Symbol, untyped] data) -> (::String | false)
45
+
46
+ def perform_job: (untyped job_instance, data: Hash[Symbol, untyped], message: untyped, ?duration: Float?) -> untyped
31
47
  end
32
48
  end
33
49
  end
data/sig/cosmo/job.rbs CHANGED
@@ -14,6 +14,8 @@ module Cosmo
14
14
 
15
15
  def limits_concurrency?: () -> bool
16
16
 
17
+ def retry_in: (?Hash[Symbol, untyped]? data) -> Proc?
18
+
17
19
  def perform: (*untyped args, ?async: bool, **untyped options) -> (::String | nil)
18
20
 
19
21
  def perform_async: (*untyped args) -> ::String
@@ -29,10 +31,18 @@ module Cosmo
29
31
  private
30
32
 
31
33
  def client: () -> Client
34
+
35
+ def publish: (Data data, Batch? batch) -> void
32
36
  end
33
37
 
34
38
  attr_accessor jid: ::String
35
39
 
40
+ attr_accessor enqueued_at: ::Time?
41
+
42
+ attr_accessor attempt: ::Integer?
43
+
44
+ attr_accessor scheduled_by: ::String?
45
+
36
46
  def perform: (*untyped) -> untyped
37
47
 
38
48
  def logger: () -> untyped
@@ -1,8 +1,8 @@
1
1
  module Cosmo
2
2
  class Processor
3
- STREAM_PAUSED_RECHECK_TTL: Float
4
- STREAMS_PAUSED_IDLE_SLEEP: Float
5
- STREAM_EMPTY_BACKOFF_MAX: Float
3
+ STREAM_PAUSED_RECHECK_TTL: ::String
4
+ STREAMS_PAUSED_IDLE_SLEEP: ::String
5
+ STREAM_EMPTY_BACKOFF_MAX: ::String
6
6
 
7
7
  @pool: Utils::ThreadPool
8
8
  @running: untyped
@@ -0,0 +1,11 @@
1
+ module Cosmo
2
+ module Utils
3
+ module Duration
4
+ UNITS: ::Hash[::String, ::Integer]
5
+
6
+ def self.parse: (Numeric | ::String value) -> Numeric
7
+
8
+ def parse: (Numeric | ::String value) -> Numeric
9
+ end
10
+ end
11
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cosmonats
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Vorotilin
@@ -37,6 +37,20 @@ dependencies:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
39
  version: '2.5'
40
+ - !ruby/object:Gem::Dependency
41
+ name: concurrent-ruby
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.3'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.3'
40
54
  - !ruby/object:Gem::Dependency
41
55
  name: rack
42
56
  requirement: !ruby/object:Gem::Requirement
@@ -68,6 +82,7 @@ files:
68
82
  - lib/cosmo/active_job/executor.rb
69
83
  - lib/cosmo/active_job/options.rb
70
84
  - lib/cosmo/api.rb
85
+ - lib/cosmo/api/batch.rb
71
86
  - lib/cosmo/api/busy.rb
72
87
  - lib/cosmo/api/counter.rb
73
88
  - lib/cosmo/api/cron.rb
@@ -76,6 +91,9 @@ files:
76
91
  - lib/cosmo/api/kv.rb
77
92
  - lib/cosmo/api/stats.rb
78
93
  - lib/cosmo/api/stream.rb
94
+ - lib/cosmo/batch.rb
95
+ - lib/cosmo/batch/callback.rb
96
+ - lib/cosmo/batch/dispatcher.rb
79
97
  - lib/cosmo/cli.rb
80
98
  - lib/cosmo/client.rb
81
99
  - lib/cosmo/config.rb
@@ -96,6 +114,7 @@ files:
96
114
  - lib/cosmo/stream/processor.rb
97
115
  - lib/cosmo/stream/serializer.rb
98
116
  - lib/cosmo/utils.rb
117
+ - lib/cosmo/utils/duration.rb
99
118
  - lib/cosmo/utils/hash.rb
100
119
  - lib/cosmo/utils/json.rb
101
120
  - lib/cosmo/utils/overrides.rb
@@ -112,12 +131,15 @@ files:
112
131
  - lib/cosmo/web/context.rb
113
132
  - lib/cosmo/web/controllers/actions.rb
114
133
  - lib/cosmo/web/controllers/application.rb
134
+ - lib/cosmo/web/controllers/batches.rb
115
135
  - lib/cosmo/web/controllers/crons.rb
116
136
  - lib/cosmo/web/controllers/jobs.rb
117
137
  - lib/cosmo/web/controllers/streams.rb
118
138
  - lib/cosmo/web/helpers/application.rb
119
139
  - lib/cosmo/web/renderer.rb
120
140
  - lib/cosmo/web/views/actions/index.erb
141
+ - lib/cosmo/web/views/batches/_table.erb
142
+ - lib/cosmo/web/views/batches/index.erb
121
143
  - lib/cosmo/web/views/crons/_table.erb
122
144
  - lib/cosmo/web/views/crons/index.erb
123
145
  - lib/cosmo/web/views/jobs/_busy.erb
@@ -143,6 +165,7 @@ files:
143
165
  - sig/cosmo/active_job/adapter.rbs
144
166
  - sig/cosmo/active_job/executor.rbs
145
167
  - sig/cosmo/active_job/options.rbs
168
+ - sig/cosmo/api/batch.rbs
146
169
  - sig/cosmo/api/busy.rbs
147
170
  - sig/cosmo/api/counter.rbs
148
171
  - sig/cosmo/api/cron.rbs
@@ -151,6 +174,9 @@ files:
151
174
  - sig/cosmo/api/kv.rbs
152
175
  - sig/cosmo/api/stats.rbs
153
176
  - sig/cosmo/api/stream.rbs
177
+ - sig/cosmo/batch.rbs
178
+ - sig/cosmo/batch/callback.rbs
179
+ - sig/cosmo/batch/dispatcher.rbs
154
180
  - sig/cosmo/cli.rbs
155
181
  - sig/cosmo/client.rbs
156
182
  - sig/cosmo/config.rbs
@@ -168,6 +194,7 @@ files:
168
194
  - sig/cosmo/stream/message.rbs
169
195
  - sig/cosmo/stream/processor.rbs
170
196
  - sig/cosmo/stream/serializer.rbs
197
+ - sig/cosmo/utils/duration.rbs
171
198
  - sig/cosmo/utils/hash.rbs
172
199
  - sig/cosmo/utils/json.rbs
173
200
  - sig/cosmo/utils/signal.rbs