cosmonats 0.7.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 +219 -113
- data/lib/cosmo/active_job/adapter.rb +1 -2
- data/lib/cosmo/api/counter.rb +17 -17
- data/lib/cosmo/api/cron/entry.rb +10 -11
- data/lib/cosmo/api/cron.rb +6 -3
- data/lib/cosmo/api/job.rb +1 -1
- data/lib/cosmo/api/kv.rb +3 -3
- data/lib/cosmo/api/registry.rb +1 -1
- data/lib/cosmo/api/stats/busy.rb +6 -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 +18 -2
- data/lib/cosmo/api/stream.rb +7 -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 +37 -14
- 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 +32 -58
- 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/utils.rb +0 -1
- data/lib/cosmo/version.rb +1 -1
- data/lib/cosmo/web/assets/app.css +271 -571
- 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 +122 -0
- 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 +17 -2
- data/lib/cosmo/web/controllers/jobs.rb +59 -54
- data/lib/cosmo/web/controllers/metrics.rb +49 -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 +67 -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 -43
- 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 +41 -0
- data/lib/cosmo/web/views/metrics/_panel.erb +13 -0
- data/lib/cosmo/web/views/metrics/_table.erb +25 -0
- data/lib/cosmo/web/views/metrics/index.erb +9 -0
- 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 +67 -33
- data/lib/cosmo.rb +38 -0
- data/sig/cosmo/api/counter.rbs +2 -3
- data/sig/cosmo/api/cron/entry.rbs +2 -1
- data/sig/cosmo/api/stats/busy.rbs +3 -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/api/stats.rbs +4 -0
- data/sig/cosmo/api/stream.rbs +2 -0
- 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 +4 -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 +60 -20
- data/lib/cosmo/sentry/auto.rb +0 -6
- 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 -131
- data/lib/cosmo/web/views/jobs/_stats.erb +0 -73
- data/lib/cosmo/web/views/jobs/_tabs.erb +0 -8
- 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
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
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dmitry Vorotilin
|
|
@@ -43,20 +43,20 @@ dependencies:
|
|
|
43
43
|
requirements:
|
|
44
44
|
- - ">="
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '2.
|
|
46
|
+
version: '2.7'
|
|
47
47
|
- - "<"
|
|
48
48
|
- !ruby/object:Gem::Version
|
|
49
|
-
version: '2.
|
|
49
|
+
version: '2.8'
|
|
50
50
|
type: :runtime
|
|
51
51
|
prerelease: false
|
|
52
52
|
version_requirements: !ruby/object:Gem::Requirement
|
|
53
53
|
requirements:
|
|
54
54
|
- - ">="
|
|
55
55
|
- !ruby/object:Gem::Version
|
|
56
|
-
version: '2.
|
|
56
|
+
version: '2.7'
|
|
57
57
|
- - "<"
|
|
58
58
|
- !ruby/object:Gem::Version
|
|
59
|
-
version: '2.
|
|
59
|
+
version: '2.8'
|
|
60
60
|
description: Lightweight background and stream processing for Ruby
|
|
61
61
|
email:
|
|
62
62
|
- d.vorotilin@gmail.com
|
|
@@ -83,15 +83,19 @@ files:
|
|
|
83
83
|
- lib/cosmo/api/registry.rb
|
|
84
84
|
- lib/cosmo/api/stats.rb
|
|
85
85
|
- lib/cosmo/api/stats/busy.rb
|
|
86
|
+
- lib/cosmo/api/stats/metrics.rb
|
|
86
87
|
- lib/cosmo/api/stats/processes.rb
|
|
87
88
|
- lib/cosmo/api/stats/totals.rb
|
|
88
89
|
- lib/cosmo/api/stream.rb
|
|
89
90
|
- lib/cosmo/batch.rb
|
|
90
91
|
- lib/cosmo/batch/callback.rb
|
|
92
|
+
- lib/cosmo/batch/counters.rb
|
|
91
93
|
- lib/cosmo/batch/dispatcher.rb
|
|
92
94
|
- lib/cosmo/cli.rb
|
|
93
95
|
- lib/cosmo/client.rb
|
|
94
96
|
- lib/cosmo/config.rb
|
|
97
|
+
- lib/cosmo/config/cosmo.yml
|
|
98
|
+
- lib/cosmo/config/settings.rb
|
|
95
99
|
- lib/cosmo/engine.rb
|
|
96
100
|
- lib/cosmo/heartbeat.rb
|
|
97
101
|
- lib/cosmo/http_server.rb
|
|
@@ -100,16 +104,24 @@ files:
|
|
|
100
104
|
- lib/cosmo/http_server/ping.rb
|
|
101
105
|
- lib/cosmo/job.rb
|
|
102
106
|
- lib/cosmo/job/data.rb
|
|
107
|
+
- lib/cosmo/job/enqueuer.rb
|
|
103
108
|
- lib/cosmo/job/failure.rb
|
|
104
109
|
- lib/cosmo/job/limit.rb
|
|
105
110
|
- lib/cosmo/job/processor.rb
|
|
111
|
+
- lib/cosmo/job/requeue.rb
|
|
106
112
|
- lib/cosmo/job/stream_filter.rb
|
|
107
113
|
- lib/cosmo/logger.rb
|
|
114
|
+
- lib/cosmo/middleware.rb
|
|
115
|
+
- lib/cosmo/middleware/busy.rb
|
|
116
|
+
- lib/cosmo/middleware/chain.rb
|
|
117
|
+
- lib/cosmo/middleware/limit.rb
|
|
118
|
+
- lib/cosmo/middleware/metrics.rb
|
|
119
|
+
- lib/cosmo/middleware/sentry.rb
|
|
120
|
+
- lib/cosmo/middleware/totals.rb
|
|
108
121
|
- lib/cosmo/processor.rb
|
|
109
122
|
- lib/cosmo/publisher.rb
|
|
110
123
|
- lib/cosmo/railtie.rb
|
|
111
|
-
- lib/cosmo/
|
|
112
|
-
- lib/cosmo/sentry/job_processor_middleware.rb
|
|
124
|
+
- lib/cosmo/services.rb
|
|
113
125
|
- lib/cosmo/stream.rb
|
|
114
126
|
- lib/cosmo/stream/data.rb
|
|
115
127
|
- lib/cosmo/stream/message.rb
|
|
@@ -119,48 +131,67 @@ files:
|
|
|
119
131
|
- lib/cosmo/utils/duration.rb
|
|
120
132
|
- lib/cosmo/utils/hash.rb
|
|
121
133
|
- lib/cosmo/utils/json.rb
|
|
122
|
-
- lib/cosmo/utils/overrides.rb
|
|
123
134
|
- lib/cosmo/utils/signal.rb
|
|
124
135
|
- lib/cosmo/utils/stopwatch.rb
|
|
125
136
|
- lib/cosmo/utils/string.rb
|
|
126
137
|
- lib/cosmo/utils/thread_pool.rb
|
|
127
138
|
- lib/cosmo/utils/ttl_cache.rb
|
|
128
|
-
- lib/cosmo/utils/warnings.rb
|
|
129
139
|
- lib/cosmo/version.rb
|
|
130
140
|
- lib/cosmo/web.rb
|
|
131
141
|
- lib/cosmo/web/assets/app.css
|
|
142
|
+
- lib/cosmo/web/assets/app.js
|
|
143
|
+
- lib/cosmo/web/assets/fonts/OFL.txt
|
|
144
|
+
- lib/cosmo/web/assets/fonts/geist-mono.woff2
|
|
145
|
+
- lib/cosmo/web/assets/fonts/geist.woff2
|
|
132
146
|
- lib/cosmo/web/assets/htmx.2.0.8.min.js.gz
|
|
147
|
+
- lib/cosmo/web/assets/logo.svg
|
|
148
|
+
- lib/cosmo/web/cache.rb
|
|
149
|
+
- lib/cosmo/web/chart.rb
|
|
150
|
+
- lib/cosmo/web/circuit.rb
|
|
133
151
|
- lib/cosmo/web/context.rb
|
|
134
|
-
- lib/cosmo/web/controllers/actions.rb
|
|
135
152
|
- lib/cosmo/web/controllers/application.rb
|
|
136
153
|
- lib/cosmo/web/controllers/batches.rb
|
|
137
154
|
- lib/cosmo/web/controllers/crons.rb
|
|
138
155
|
- lib/cosmo/web/controllers/jobs.rb
|
|
156
|
+
- lib/cosmo/web/controllers/metrics.rb
|
|
157
|
+
- lib/cosmo/web/controllers/overview.rb
|
|
158
|
+
- lib/cosmo/web/controllers/processes.rb
|
|
139
159
|
- lib/cosmo/web/controllers/streams.rb
|
|
140
160
|
- lib/cosmo/web/helpers/application.rb
|
|
141
161
|
- lib/cosmo/web/renderer.rb
|
|
142
|
-
- lib/cosmo/web/
|
|
162
|
+
- lib/cosmo/web/throughput.rb
|
|
143
163
|
- lib/cosmo/web/views/batches/_table.erb
|
|
144
164
|
- lib/cosmo/web/views/batches/index.erb
|
|
145
165
|
- lib/cosmo/web/views/crons/_table.erb
|
|
146
166
|
- lib/cosmo/web/views/crons/index.erb
|
|
167
|
+
- lib/cosmo/web/views/jobs/_args.erb
|
|
147
168
|
- lib/cosmo/web/views/jobs/_busy.erb
|
|
148
169
|
- lib/cosmo/web/views/jobs/_dead.erb
|
|
149
170
|
- lib/cosmo/web/views/jobs/_enqueued.erb
|
|
150
|
-
- lib/cosmo/web/views/jobs/
|
|
171
|
+
- lib/cosmo/web/views/jobs/_panel.erb
|
|
172
|
+
- lib/cosmo/web/views/jobs/_queue_row.erb
|
|
173
|
+
- lib/cosmo/web/views/jobs/_queues.erb
|
|
151
174
|
- lib/cosmo/web/views/jobs/_scheduled.erb
|
|
152
|
-
- lib/cosmo/web/views/jobs/_stats.erb
|
|
153
|
-
- lib/cosmo/web/views/jobs/_tabs.erb
|
|
154
|
-
- lib/cosmo/web/views/jobs/busy.erb
|
|
155
|
-
- lib/cosmo/web/views/jobs/dead.erb
|
|
156
|
-
- lib/cosmo/web/views/jobs/enqueued.erb
|
|
157
175
|
- lib/cosmo/web/views/jobs/index.erb
|
|
158
|
-
- lib/cosmo/web/views/jobs/scheduled.erb
|
|
159
176
|
- lib/cosmo/web/views/layout.erb
|
|
177
|
+
- lib/cosmo/web/views/metrics/_chart.erb
|
|
178
|
+
- lib/cosmo/web/views/metrics/_panel.erb
|
|
179
|
+
- lib/cosmo/web/views/metrics/_table.erb
|
|
180
|
+
- lib/cosmo/web/views/metrics/index.erb
|
|
181
|
+
- lib/cosmo/web/views/overview/_badges.erb
|
|
182
|
+
- lib/cosmo/web/views/overview/_kpis.erb
|
|
183
|
+
- lib/cosmo/web/views/overview/_running.erb
|
|
184
|
+
- lib/cosmo/web/views/overview/_throughput.erb
|
|
185
|
+
- lib/cosmo/web/views/overview/_workers.erb
|
|
186
|
+
- lib/cosmo/web/views/overview/index.erb
|
|
187
|
+
- lib/cosmo/web/views/processes/_table.erb
|
|
188
|
+
- lib/cosmo/web/views/processes/index.erb
|
|
189
|
+
- lib/cosmo/web/views/shared/_pager.erb
|
|
190
|
+
- lib/cosmo/web/views/shared/unavailable.erb
|
|
160
191
|
- lib/cosmo/web/views/streams/_info.erb
|
|
192
|
+
- lib/cosmo/web/views/streams/_list.erb
|
|
161
193
|
- lib/cosmo/web/views/streams/_pause_banner.erb
|
|
162
|
-
- lib/cosmo/web/views/streams/
|
|
163
|
-
- lib/cosmo/web/views/streams/_table.erb
|
|
194
|
+
- lib/cosmo/web/views/streams/_stream.erb
|
|
164
195
|
- lib/cosmo/web/views/streams/index.erb
|
|
165
196
|
- lib/cosmo/web/views/streams/info.erb
|
|
166
197
|
- lib/cosmonats.rb
|
|
@@ -177,15 +208,18 @@ files:
|
|
|
177
208
|
- sig/cosmo/api/registry.rbs
|
|
178
209
|
- sig/cosmo/api/stats.rbs
|
|
179
210
|
- sig/cosmo/api/stats/busy.rbs
|
|
211
|
+
- sig/cosmo/api/stats/metrics.rbs
|
|
180
212
|
- sig/cosmo/api/stats/processes.rbs
|
|
181
213
|
- sig/cosmo/api/stats/totals.rbs
|
|
182
214
|
- sig/cosmo/api/stream.rbs
|
|
183
215
|
- sig/cosmo/batch.rbs
|
|
184
216
|
- sig/cosmo/batch/callback.rbs
|
|
217
|
+
- sig/cosmo/batch/counters.rbs
|
|
185
218
|
- sig/cosmo/batch/dispatcher.rbs
|
|
186
219
|
- sig/cosmo/cli.rbs
|
|
187
220
|
- sig/cosmo/client.rbs
|
|
188
221
|
- sig/cosmo/config.rbs
|
|
222
|
+
- sig/cosmo/config/settings.rbs
|
|
189
223
|
- sig/cosmo/engine.rbs
|
|
190
224
|
- sig/cosmo/heartbeat.rbs
|
|
191
225
|
- sig/cosmo/http_server.rbs
|
|
@@ -194,14 +228,20 @@ files:
|
|
|
194
228
|
- sig/cosmo/http_server/ping.rbs
|
|
195
229
|
- sig/cosmo/job.rbs
|
|
196
230
|
- sig/cosmo/job/data.rbs
|
|
231
|
+
- sig/cosmo/job/enqueuer.rbs
|
|
197
232
|
- sig/cosmo/job/failure.rbs
|
|
198
233
|
- sig/cosmo/job/limit.rbs
|
|
199
234
|
- sig/cosmo/job/processor.rbs
|
|
235
|
+
- sig/cosmo/job/requeue.rbs
|
|
200
236
|
- sig/cosmo/job/stream_filter.rbs
|
|
201
237
|
- sig/cosmo/logger.rbs
|
|
238
|
+
- sig/cosmo/middleware.rbs
|
|
239
|
+
- sig/cosmo/middleware/chain.rbs
|
|
240
|
+
- sig/cosmo/middleware/sentry.rbs
|
|
202
241
|
- sig/cosmo/processor.rbs
|
|
203
242
|
- sig/cosmo/publisher.rbs
|
|
204
243
|
- sig/cosmo/railtie.rbs
|
|
244
|
+
- sig/cosmo/services.rbs
|
|
205
245
|
- sig/cosmo/stream.rbs
|
|
206
246
|
- sig/cosmo/stream/data.rbs
|
|
207
247
|
- sig/cosmo/stream/message.rbs
|
data/lib/cosmo/sentry/auto.rb
DELETED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
Cosmo::Utils::Warnings.silence do
|
|
4
|
-
unless NATS::JetStream::API::StreamConfig.members.include?(:allow_msg_schedules)
|
|
5
|
-
members = NATS::JetStream::API::StreamConfig.members + %i[allow_msg_counter allow_msg_schedules]
|
|
6
|
-
NATS::JetStream::API::StreamConfig = Struct.new(*members, keyword_init: true) do
|
|
7
|
-
def initialize(opts = {})
|
|
8
|
-
rem = opts.keys - members
|
|
9
|
-
opts.delete_if { |k| rem.include?(k) }
|
|
10
|
-
super
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
unless NATS::JetStream::PubAck.members.include?(:val)
|
|
16
|
-
members = NATS::JetStream::PubAck.members + [:val]
|
|
17
|
-
NATS::JetStream::PubAck = Struct.new(*members, keyword_init: true)
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Upstream bug in nats-pure 2.5.0 and 2.6.0 (https://github.com/nats-io/nats-pure.rb):
|
|
22
|
-
# NATS::JetStream::PullSubscription#fetch only assigns its `start_time` local in the
|
|
23
|
-
# `batch > 1` branch, but the final "did we time out" check outside the case statement
|
|
24
|
-
# reads it unconditionally. For `batch == 1` (what Cosmo::Processor always uses),
|
|
25
|
-
# `start_time` is nil there -- normally masked because the `batch == 1` branch has its
|
|
26
|
-
# own timeout check (using a properly-set `t`) that raises first.
|
|
27
|
-
#
|
|
28
|
-
# It only surfaces once two threads call #fetch concurrently on the *same* subscription:
|
|
29
|
-
# @pending_queue/wait_for_msgs_cond are shared per-subscription state, and #dispatch's
|
|
30
|
-
# `wait_for_msgs_cond.signal` wakes exactly one arbitrary waiter -- not necessarily the
|
|
31
|
-
# one whose own request produced that wakeup. A thread woken by someone else's signal
|
|
32
|
-
# finds nothing left in the (already-drained) queue, so it skips its own inner timeout
|
|
33
|
-
# check and falls into the buggy tail check, raising `TypeError: nil can't be coerced
|
|
34
|
-
# into Float` instead of the intended timeout/empty result. Reproduced deterministically
|
|
35
|
-
# with N threads fetching in a loop against the same PullSubscription.
|
|
36
|
-
#
|
|
37
|
-
# Second, separate upstream bug in the same method: the post-wait timeout check below
|
|
38
|
-
# (`raise ... if MonotonicTime.since(t) > timeout`) fires unconditionally, unlike every
|
|
39
|
-
# other timeout check in this method (all guarded with `msgs.empty? &&`). If the unsynchronized
|
|
40
|
-
# pop just above it succeeded in grabbing a real, already-delivered message -- which can happen
|
|
41
|
-
# even after `timeout` has technically elapsed, since #dispatch's signal and this thread actually
|
|
42
|
-
# resuming are two different moments -- that message is already gone from @pending_queue with
|
|
43
|
-
# nowhere else to go, yet gets discarded here and a plain NATS::Timeout raised instead. Callers
|
|
44
|
-
# (including Cosmo::Processor#fetch) treat NATS::Timeout as "no messages", so the message is lost
|
|
45
|
-
# silently: never processed, never acked, and not redelivered until the consumer's ack_wait expires.
|
|
46
|
-
# Reproduces reliably with several threads fetching (batch: 1) concurrently against the same
|
|
47
|
-
# subscription, e.g. Cosmo::Job::Processor's thread pool.
|
|
48
|
-
#
|
|
49
|
-
# Vendored copy of nats-pure's PullSubscription#fetch, kept as close to
|
|
50
|
-
# upstream as possible (two one-line fixes, see comments above), so it's easy to diff against the
|
|
51
|
-
# next nats-pure release and drop once fixed there. Fixed on nats-pure main by the fetch rewrite
|
|
52
|
-
# (#181), recognized by its private #take_pending, in which case this copy is not loaded.
|
|
53
|
-
return if NATS::JetStream.const_get(:PullSubscription).private_method_defined?(:take_pending)
|
|
54
|
-
|
|
55
|
-
# rubocop:disable all
|
|
56
|
-
module NATS
|
|
57
|
-
class JetStream
|
|
58
|
-
module PullSubscription
|
|
59
|
-
def fetch(batch = 1, params = {})
|
|
60
|
-
raise ::NATS::JetStream::Error.new("nats: invalid batch size") if batch < 1
|
|
61
|
-
|
|
62
|
-
t = MonotonicTime.now
|
|
63
|
-
start_time = t # fixes the upstream bug -- see comment above
|
|
64
|
-
timeout = params[:timeout] ||= 5
|
|
65
|
-
expires = (timeout * 1_000_000_000) - 100_000
|
|
66
|
-
next_req = { batch: batch }
|
|
67
|
-
|
|
68
|
-
msgs = []
|
|
69
|
-
case
|
|
70
|
-
when batch == 1
|
|
71
|
-
synchronize do
|
|
72
|
-
unless @pending_queue.empty?
|
|
73
|
-
msg = @pending_queue.pop
|
|
74
|
-
@pending_size -= msg.data.size
|
|
75
|
-
if JS.is_status_msg(msg)
|
|
76
|
-
case msg.header["Status"]
|
|
77
|
-
when JS::Status::NoMsgs then nil
|
|
78
|
-
when JS::Status::RequestTimeout then nil # Skip
|
|
79
|
-
else raise JS.from_msg(msg)
|
|
80
|
-
end
|
|
81
|
-
else
|
|
82
|
-
msgs << msg
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
next_req[:expires] = expires
|
|
88
|
-
if msgs.empty?
|
|
89
|
-
@nc.publish(@jsi.nms, JS.next_req_to_json(next_req), @subject)
|
|
90
|
-
synchronize { wait_for_msgs_cond.wait(timeout) }
|
|
91
|
-
|
|
92
|
-
unless @pending_queue.empty?
|
|
93
|
-
msg = @pending_queue.pop
|
|
94
|
-
@pending_size -= msg.data.size
|
|
95
|
-
msgs << msg
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
raise ::NATS::Timeout.new("nats: fetch timeout") if msgs.empty? && (MonotonicTime.since(t) > timeout)
|
|
99
|
-
|
|
100
|
-
if JS.is_status_msg(msgs.first)
|
|
101
|
-
msg = msgs.first
|
|
102
|
-
case msg.header[JS::Header::Status]
|
|
103
|
-
when JS::Status::RequestTimeout then raise NATS::Timeout.new("nats: fetch request timeout")
|
|
104
|
-
else raise JS.from_msg(msgs.first)
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
when batch > 1
|
|
109
|
-
synchronize do
|
|
110
|
-
if batch <= @pending_queue.size
|
|
111
|
-
batch.times do
|
|
112
|
-
msg = @pending_queue.pop
|
|
113
|
-
@pending_size -= msg.data.size
|
|
114
|
-
if JS.is_status_msg(msg)
|
|
115
|
-
case msg.header[JS::Header::Status]
|
|
116
|
-
when JS::Status::NoMsgs, JS::Status::RequestTimeout then next
|
|
117
|
-
else raise JS.from_msg(msg)
|
|
118
|
-
end
|
|
119
|
-
else
|
|
120
|
-
msgs << msg
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
return msgs
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
next_req[:no_wait] = true
|
|
128
|
-
@nc.publish(@jsi.nms, JS.next_req_to_json(next_req), @subject)
|
|
129
|
-
|
|
130
|
-
start_time = MonotonicTime.now
|
|
131
|
-
msg = nil
|
|
132
|
-
|
|
133
|
-
synchronize do
|
|
134
|
-
wait_for_msgs_cond.wait(timeout)
|
|
135
|
-
unless @pending_queue.empty?
|
|
136
|
-
msg = @pending_queue.pop
|
|
137
|
-
@pending_size -= msg.data.size
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
if !msg.nil? && JS.is_status_msg(msg)
|
|
142
|
-
case msg.header[JS::Header::Status]
|
|
143
|
-
when JS::Status::NoMsgs
|
|
144
|
-
next_req[:expires] = expires
|
|
145
|
-
next_req.delete(:no_wait)
|
|
146
|
-
@nc.publish(@jsi.nms, JS.next_req_to_json(next_req), @subject)
|
|
147
|
-
when JS::Status::RequestTimeout
|
|
148
|
-
raise NATS::Timeout.new("nats: fetch request timeout")
|
|
149
|
-
else
|
|
150
|
-
raise JS.from_msg(msg)
|
|
151
|
-
end
|
|
152
|
-
else
|
|
153
|
-
msgs << msg unless msg.nil?
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
duration = MonotonicTime.since(start_time)
|
|
157
|
-
raise NATS::Timeout.new("nats: fetch timeout") if msgs.empty? && (duration > timeout)
|
|
158
|
-
|
|
159
|
-
needed = batch - msgs.count
|
|
160
|
-
while (needed > 0) && (MonotonicTime.since(start_time) < timeout)
|
|
161
|
-
duration = MonotonicTime.since(start_time)
|
|
162
|
-
|
|
163
|
-
synchronize do
|
|
164
|
-
if @pending_queue.empty?
|
|
165
|
-
deadline = timeout - duration
|
|
166
|
-
wait_for_msgs_cond.wait(deadline) if deadline > 0
|
|
167
|
-
|
|
168
|
-
duration = MonotonicTime.since(start_time)
|
|
169
|
-
if msgs.empty? && @pending_queue.empty? && (duration > timeout)
|
|
170
|
-
raise NATS::Timeout.new("nats: fetch timeout")
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
unless @pending_queue.empty?
|
|
175
|
-
msg = @pending_queue.pop
|
|
176
|
-
@pending_size -= msg.data.size
|
|
177
|
-
|
|
178
|
-
if JS.is_status_msg(msg)
|
|
179
|
-
case msg.header[JS::Header::Status]
|
|
180
|
-
when JS::Status::NoMsgs, JS::Status::RequestTimeout
|
|
181
|
-
duration = MonotonicTime.since(start_time)
|
|
182
|
-
if duration > timeout
|
|
183
|
-
raise NATS::Timeout.new("nats: fetch timeout") if msgs.empty?
|
|
184
|
-
|
|
185
|
-
return msgs
|
|
186
|
-
end
|
|
187
|
-
else
|
|
188
|
-
raise JS.from_msg(msg)
|
|
189
|
-
end
|
|
190
|
-
else
|
|
191
|
-
msgs << msg
|
|
192
|
-
needed -= 1
|
|
193
|
-
end
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
raise ::NATS::Timeout.new("nats: fetch timeout") if msgs.empty? && (MonotonicTime.since(start_time) > timeout)
|
|
200
|
-
|
|
201
|
-
msgs
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
end
|
|
206
|
-
# rubocop:enable all
|
data/lib/cosmo/utils/warnings.rb
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "cosmo/web/controllers/application"
|
|
4
|
-
|
|
5
|
-
module Cosmo
|
|
6
|
-
class Web
|
|
7
|
-
module Controllers
|
|
8
|
-
class Actions < Application
|
|
9
|
-
def index
|
|
10
|
-
content_for :title, "Actions"
|
|
11
|
-
ok render("actions/index", layout: true)
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
<% nav = @polling ? { limit: @limit } : { limit: @limit, poll: 0 } -%>
|
|
2
|
-
<% states = { "running" => "badge-success", "quiet" => "badge-warning", "stopping" => "badge-danger" } -%>
|
|
3
|
-
<div id="processes-poller"
|
|
4
|
-
<% if @polling -%>
|
|
5
|
-
hx-get="<%= url_for('/jobs/_processes', page: @page, limit: @limit) %>"
|
|
6
|
-
hx-trigger="every <%= poll_interval %>s"
|
|
7
|
-
hx-swap="outerHTML"
|
|
8
|
-
<% end -%>
|
|
9
|
-
>
|
|
10
|
-
<div class="pending">
|
|
11
|
-
<span></span>
|
|
12
|
-
<span class="poll-status">
|
|
13
|
-
<span class="text-muted"><%= @total %> process(es) alive</span>
|
|
14
|
-
<% if @polling -%>
|
|
15
|
-
<a class="btn btn-sm"
|
|
16
|
-
hx-get="<%= url_for('/jobs/_processes', nav.merge(page: @page, poll: 0)) %>"
|
|
17
|
-
hx-target="#processes-poller"
|
|
18
|
-
hx-swap="outerHTML"
|
|
19
|
-
hx-indicator="#global-spinner">⏹ Stop Polling</a>
|
|
20
|
-
<% else -%>
|
|
21
|
-
<a class="btn btn-sm btn-primary"
|
|
22
|
-
hx-get="<%= url_for('/jobs/_processes', page: @page, limit: @limit) %>"
|
|
23
|
-
hx-target="#processes-poller"
|
|
24
|
-
hx-swap="outerHTML"
|
|
25
|
-
hx-indicator="#global-spinner">▶ Live Poll</a>
|
|
26
|
-
<% end -%>
|
|
27
|
-
</span>
|
|
28
|
-
</div>
|
|
29
|
-
|
|
30
|
-
<% if @processes.empty? -%>
|
|
31
|
-
<div class="alert alert-warning">No worker processes are running</div>
|
|
32
|
-
<% else -%>
|
|
33
|
-
<div class="table-container">
|
|
34
|
-
<table>
|
|
35
|
-
<thead>
|
|
36
|
-
<tr>
|
|
37
|
-
<th>Process</th>
|
|
38
|
-
<th>Command</th>
|
|
39
|
-
<th>Subscriptions</th>
|
|
40
|
-
<th>Busy</th>
|
|
41
|
-
<th>State</th>
|
|
42
|
-
<th>RTT</th>
|
|
43
|
-
<th>Reconnects</th>
|
|
44
|
-
<th>Memory</th>
|
|
45
|
-
<th>Uptime</th>
|
|
46
|
-
<th>Last beat</th>
|
|
47
|
-
</tr>
|
|
48
|
-
</thead>
|
|
49
|
-
<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] -%>· <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>
|
|
68
|
-
<div class="text-muted" style="font-size: var(--font-size-small);">
|
|
69
|
-
cosmo <%= h(process[:version]) %> · <%= h(process[:ruby]) %>
|
|
70
|
-
<% if process[:http_port] -%>· http :<%= h(process[:http_port]) %><% end -%>
|
|
71
|
-
</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" : '—' %></td>
|
|
83
|
-
<td><%= h(nats[:reconnects].to_i) %></td>
|
|
84
|
-
<td><%= process[:rss] ? format_bytes(process[:rss].to_i * 1024) : '—' %></td>
|
|
85
|
-
<td><%= elapsed(process[:started_at]) %></td>
|
|
86
|
-
<td><%= elapsed(process[:beat_at]) %> ago</td>
|
|
87
|
-
</tr>
|
|
88
|
-
<% end -%>
|
|
89
|
-
</tbody>
|
|
90
|
-
</table>
|
|
91
|
-
</div>
|
|
92
|
-
<% end -%>
|
|
93
|
-
|
|
94
|
-
<% if @total_pages.to_i > 1 -%>
|
|
95
|
-
<div class="pagination">
|
|
96
|
-
<% if @page > 1 -%>
|
|
97
|
-
<a hx-get="<%= url_for('/jobs/_processes', nav.merge(page: @page - 1)) %>"
|
|
98
|
-
hx-target="#processes-poller"
|
|
99
|
-
hx-swap="outerHTML"
|
|
100
|
-
hx-indicator="#global-spinner"
|
|
101
|
-
class="btn">← Prev</a>
|
|
102
|
-
<% else -%>
|
|
103
|
-
<span class="btn btn-disabled">← Prev</span>
|
|
104
|
-
<% end -%>
|
|
105
|
-
|
|
106
|
-
<% pages(@page, @total_pages).each do |p| -%>
|
|
107
|
-
<% if p == :gap -%>
|
|
108
|
-
<span class="pagination-gap">…</span>
|
|
109
|
-
<% elsif p == @page -%>
|
|
110
|
-
<span class="btn btn-primary btn-disabled"><%= p %></span>
|
|
111
|
-
<% else -%>
|
|
112
|
-
<a hx-get="<%= url_for('/jobs/_processes', nav.merge(page: p)) %>"
|
|
113
|
-
hx-target="#processes-poller"
|
|
114
|
-
hx-swap="outerHTML"
|
|
115
|
-
hx-indicator="#global-spinner"
|
|
116
|
-
class="btn"><%= p %></a>
|
|
117
|
-
<% end -%>
|
|
118
|
-
<% end -%>
|
|
119
|
-
|
|
120
|
-
<% if @page < @total_pages -%>
|
|
121
|
-
<a hx-get="<%= url_for('/jobs/_processes', nav.merge(page: @page + 1)) %>"
|
|
122
|
-
hx-target="#processes-poller"
|
|
123
|
-
hx-swap="outerHTML"
|
|
124
|
-
hx-indicator="#global-spinner"
|
|
125
|
-
class="btn">Next →</a>
|
|
126
|
-
<% else -%>
|
|
127
|
-
<span class="btn btn-disabled">Next →</span>
|
|
128
|
-
<% end -%>
|
|
129
|
-
</div>
|
|
130
|
-
<% end -%>
|
|
131
|
-
</div>
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
<div class="cards-container">
|
|
2
|
-
<article class="stat-card">
|
|
3
|
-
<div class="row">
|
|
4
|
-
<p>💪 Processed</p>
|
|
5
|
-
<h4><%= format_numbers(@processed) %></h4>
|
|
6
|
-
</div>
|
|
7
|
-
<div class="row">
|
|
8
|
-
<p>😥 Failed</p>
|
|
9
|
-
<h4><%= format_numbers(@failed) %></h4>
|
|
10
|
-
</div>
|
|
11
|
-
<div class="row">
|
|
12
|
-
<p>🔄 Retries</p>
|
|
13
|
-
<h4><%= format_numbers(@retries) %></h4>
|
|
14
|
-
</div>
|
|
15
|
-
</article>
|
|
16
|
-
<article class="stat-card">
|
|
17
|
-
<h3><%= format_numbers(@busy) %></h3>
|
|
18
|
-
<p>
|
|
19
|
-
<a href="<%= url_for('/jobs/busy') %>"
|
|
20
|
-
hx-get="<%= url_for('/jobs/busy') %>"
|
|
21
|
-
hx-target="#content"
|
|
22
|
-
hx-push-url="true"
|
|
23
|
-
hx-indicator="#global-spinner">⏳ Busy</a>
|
|
24
|
-
</p>
|
|
25
|
-
</article>
|
|
26
|
-
<article class="stat-card">
|
|
27
|
-
<h3><%= format_numbers(@enqueued) %></h3>
|
|
28
|
-
<p>
|
|
29
|
-
<a href="<%= url_for('/jobs/enqueued') %>"
|
|
30
|
-
hx-get="<%= url_for('/jobs/enqueued') %>"
|
|
31
|
-
hx-target="#content"
|
|
32
|
-
hx-push-url="true"
|
|
33
|
-
hx-indicator="#global-spinner"
|
|
34
|
-
class="<%= 'active' if referrer?('/jobs/enqueued') %>">📬 Enqueued</a>
|
|
35
|
-
</p>
|
|
36
|
-
</article>
|
|
37
|
-
<article class="stat-card">
|
|
38
|
-
<h3><%= format_numbers(@scheduled) %></h3>
|
|
39
|
-
<p>
|
|
40
|
-
<a href="<%= url_for('/jobs/scheduled') %>"
|
|
41
|
-
hx-get="<%= url_for('/jobs/scheduled') %>"
|
|
42
|
-
hx-target="#content"
|
|
43
|
-
hx-push-url="true"
|
|
44
|
-
hx-indicator="#global-spinner"
|
|
45
|
-
class="<%= 'active' if referrer?('/jobs/scheduled') %>">⏰ Scheduled</a>
|
|
46
|
-
</p>
|
|
47
|
-
</article>
|
|
48
|
-
<article class="stat-card">
|
|
49
|
-
<h3><%= format_numbers(@dead) %></h3>
|
|
50
|
-
<p>
|
|
51
|
-
<a href="<%= url_for('/jobs/dead') %>"
|
|
52
|
-
hx-get="<%= url_for('/jobs/dead') %>"
|
|
53
|
-
hx-target="#content"
|
|
54
|
-
hx-push-url="true"
|
|
55
|
-
hx-indicator="#global-spinner"
|
|
56
|
-
class="<%= 'active' if referrer?('/jobs/dead') %>">💀 Dead</a>
|
|
57
|
-
</p>
|
|
58
|
-
</article>
|
|
59
|
-
</div>
|
|
60
|
-
|
|
61
|
-
<script>
|
|
62
|
-
(function () {
|
|
63
|
-
const links = document.querySelectorAll('.cards-container a');
|
|
64
|
-
|
|
65
|
-
links.forEach(link => {
|
|
66
|
-
link.addEventListener('click', () => {
|
|
67
|
-
links.forEach(item => item.classList.remove('active'));
|
|
68
|
-
|
|
69
|
-
link.classList.add('active');
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
})();
|
|
73
|
-
</script>
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
<nav class="tabs">
|
|
2
|
-
<a href="<%= url_for('/jobs') %>"
|
|
3
|
-
class="<%= 'active' if path_prefix?('/jobs') %>">📋 Dashboard</a>
|
|
4
|
-
<a href="<%= url_for('/batches') %>"
|
|
5
|
-
class="<%= 'active' if path_prefix?('/batches') %>">📦 Batches</a>
|
|
6
|
-
<a href="<%= url_for('/crons') %>"
|
|
7
|
-
class="<%= 'active' if path_prefix?('/crons') %>">⏰ Crons</a>
|
|
8
|
-
</nav>
|