karafka-web 0.5.2 → 0.6.1
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
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +69 -6
- data/Gemfile.lock +14 -14
- data/karafka-web.gemspec +3 -3
- data/lib/karafka/web/config.rb +11 -5
- data/lib/karafka/web/installer.rb +2 -3
- data/lib/karafka/web/tracking/consumers/contracts/consumer_group.rb +1 -1
- data/lib/karafka/web/tracking/consumers/contracts/job.rb +4 -1
- data/lib/karafka/web/tracking/consumers/contracts/partition.rb +1 -1
- data/lib/karafka/web/tracking/consumers/contracts/report.rb +8 -4
- data/lib/karafka/web/tracking/consumers/contracts/subscription_group.rb +1 -1
- data/lib/karafka/web/tracking/consumers/contracts/topic.rb +3 -1
- data/lib/karafka/web/tracking/consumers/listeners/base.rb +2 -2
- data/lib/karafka/web/tracking/consumers/listeners/errors.rb +8 -44
- data/lib/karafka/web/tracking/consumers/listeners/processing.rb +5 -0
- data/lib/karafka/web/tracking/consumers/reporter.rb +151 -0
- data/lib/karafka/web/tracking/consumers/sampler.rb +2 -1
- data/lib/karafka/web/tracking/contracts/base.rb +34 -0
- data/lib/karafka/web/tracking/contracts/error.rb +31 -0
- data/lib/karafka/web/tracking/helpers/error_info.rb +50 -0
- data/lib/karafka/web/tracking/memoized_shell.rb +1 -1
- data/lib/karafka/web/tracking/producers/listeners/base.rb +33 -0
- data/lib/karafka/web/tracking/producers/listeners/errors.rb +66 -0
- data/lib/karafka/web/tracking/producers/listeners/reporter.rb +21 -0
- data/lib/karafka/web/tracking/producers/reporter.rb +101 -0
- data/lib/karafka/web/tracking/producers/sampler.rb +42 -0
- data/lib/karafka/web/tracking/sampler.rb +5 -0
- data/lib/karafka/web/ui/controllers/consumers.rb +2 -4
- data/lib/karafka/web/ui/models/counters.rb +51 -0
- data/lib/karafka/web/ui/models/status.rb +31 -7
- data/lib/karafka/web/ui/pro/controllers/consumers.rb +2 -3
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_job.erb +6 -6
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_metrics.erb +6 -1
- data/lib/karafka/web/ui/pro/views/consumers/index.erb +25 -21
- data/lib/karafka/web/ui/pro/views/consumers/jobs.erb +1 -1
- data/lib/karafka/web/ui/pro/views/errors/_breadcrumbs.erb +1 -2
- data/lib/karafka/web/ui/pro/views/errors/_error.erb +8 -6
- data/lib/karafka/web/ui/pro/views/errors/show.erb +3 -2
- data/lib/karafka/web/ui/public/stylesheets/application.css +4 -0
- data/lib/karafka/web/ui/views/consumers/_no_consumers.erb +9 -0
- data/lib/karafka/web/ui/views/consumers/index.erb +24 -20
- data/lib/karafka/web/ui/views/errors/_breadcrumbs.erb +1 -2
- data/lib/karafka/web/ui/views/errors/_detail.erb +9 -1
- data/lib/karafka/web/ui/views/errors/_error.erb +8 -6
- data/lib/karafka/web/ui/views/errors/show.erb +50 -2
- data/lib/karafka/web/ui/views/shared/_feature_pro.erb +4 -0
- data/lib/karafka/web/ui/views/shared/_pagination.erb +8 -2
- data/lib/karafka/web/ui/views/shared/exceptions/pro_only.erb +0 -4
- data/lib/karafka/web/ui/views/status/failures/_initial_state.erb +1 -10
- data/lib/karafka/web/ui/views/status/info/_components.erb +6 -1
- data/lib/karafka/web/ui/views/status/show.erb +6 -1
- data/lib/karafka/web/ui/views/status/successes/_connection.erb +1 -0
- data/lib/karafka/web/ui/views/status/warnings/_connection.erb +11 -0
- data/lib/karafka/web/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +28 -16
- metadata.gz.sig +0 -0
- data/lib/karafka/web/tracking/base_contract.rb +0 -31
- data/lib/karafka/web/tracking/reporter.rb +0 -144
- data/lib/karafka/web/ui/pro/views/consumers/_summary.erb +0 -81
- data/lib/karafka/web/ui/pro/views/errors/_cleaned.erb +0 -3
- data/lib/karafka/web/ui/pro/views/errors/_detail.erb +0 -31
- data/lib/karafka/web/ui/pro/views/errors/_no_errors.erb +0 -3
- data/lib/karafka/web/ui/pro/views/jobs/_breadcrumbs.erb +0 -5
- data/lib/karafka/web/ui/views/consumers/_breadcrumbs.erb +0 -27
@@ -1,144 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Karafka
|
4
|
-
module Web
|
5
|
-
module Tracking
|
6
|
-
# Reports the collected data about the process and sends it, so we can use it in the UI
|
7
|
-
class Reporter
|
8
|
-
include ::Karafka::Core::Helpers::Time
|
9
|
-
include ::Karafka::Helpers::Async
|
10
|
-
|
11
|
-
# Minimum number of messages to produce to produce then in sync mode
|
12
|
-
# This acts as a small back-off not to overload the system in case we would have extremely
|
13
|
-
# big number of errors happening
|
14
|
-
PRODUCE_SYNC_THRESHOLD = 25
|
15
|
-
|
16
|
-
private_constant :PRODUCE_SYNC_THRESHOLD
|
17
|
-
|
18
|
-
# This mutex is shared between tracker and samplers so there is no case where metrics
|
19
|
-
# would be collected same time tracker reports
|
20
|
-
MUTEX = Mutex.new
|
21
|
-
|
22
|
-
def initialize
|
23
|
-
# Move back so first report is dispatched fast to indicate, that the process is alive
|
24
|
-
@tracked_at = monotonic_now - 10_000
|
25
|
-
@consumer_contract = Consumers::Contracts::Report.new
|
26
|
-
end
|
27
|
-
|
28
|
-
# Dispatches the current state from sampler to appropriate topics
|
29
|
-
#
|
30
|
-
# @param forced [Boolean] should we report bypassing the time frequency or should we report
|
31
|
-
# only in case we would not send the report for long enough time.
|
32
|
-
def report(forced: false)
|
33
|
-
MUTEX.synchronize do
|
34
|
-
# Start background thread only when needed
|
35
|
-
# This prevents us from starting it too early or for non-consumer processes where
|
36
|
-
# Karafka is being included
|
37
|
-
async_call unless @running
|
38
|
-
|
39
|
-
return unless report?(forced)
|
40
|
-
|
41
|
-
@tracked_at = monotonic_now
|
42
|
-
|
43
|
-
consumer_report = consumer_sampler.to_report
|
44
|
-
|
45
|
-
@consumer_contract.validate!(consumer_report)
|
46
|
-
|
47
|
-
process_name = consumer_report[:process][:name]
|
48
|
-
|
49
|
-
# Report consumers statuses
|
50
|
-
messages = [
|
51
|
-
{
|
52
|
-
topic: ::Karafka::Web.config.topics.consumers.reports,
|
53
|
-
payload: consumer_report.to_json,
|
54
|
-
key: process_name,
|
55
|
-
partition: 0
|
56
|
-
}
|
57
|
-
]
|
58
|
-
|
59
|
-
# Report errors that occurred (if any)
|
60
|
-
messages += consumer_sampler.errors.map do |error|
|
61
|
-
{
|
62
|
-
topic: Karafka::Web.config.topics.errors,
|
63
|
-
payload: error.to_json,
|
64
|
-
# Always dispatch errors from the same process to the same partition
|
65
|
-
key: process_name
|
66
|
-
}
|
67
|
-
end
|
68
|
-
|
69
|
-
produce(messages)
|
70
|
-
|
71
|
-
# Clear the sampler so it tracks new state changes without previous once impacting
|
72
|
-
# the data
|
73
|
-
consumer_sampler.clear
|
74
|
-
end
|
75
|
-
# Since we run this in a background thread, there may be a case upon shutdown, where the
|
76
|
-
# producer is closed right before a potential dispatch. It is not worth dealing with this
|
77
|
-
# and we can just safely ignore this
|
78
|
-
rescue WaterDrop::Errors::ProducerClosedError
|
79
|
-
nil
|
80
|
-
end
|
81
|
-
|
82
|
-
# Reports bypassing frequency check. This can be used to report when state changes in the
|
83
|
-
# process drastically. For example when process is stopping, we want to indicate this as
|
84
|
-
# fast as possible in the UI, etc.
|
85
|
-
def report!
|
86
|
-
report(forced: true)
|
87
|
-
end
|
88
|
-
|
89
|
-
private
|
90
|
-
|
91
|
-
# Reports the process state once in a while
|
92
|
-
def call
|
93
|
-
@running = true
|
94
|
-
|
95
|
-
loop do
|
96
|
-
report
|
97
|
-
|
98
|
-
# We won't track more often anyhow but want to try frequently not to miss a window
|
99
|
-
# We need to convert the sleep interval into seconds for sleep
|
100
|
-
sleep(::Karafka::Web.config.tracking.interval / 1_000 / 10)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
# @param forced [Boolean] is this report forced. Forced means that as long as we can flush
|
105
|
-
# we will flush
|
106
|
-
# @return [Boolean] Should we report or is it not yet time to do so
|
107
|
-
def report?(forced)
|
108
|
-
# We never report in initializing phase because things are not yet fully configured
|
109
|
-
return false if ::Karafka::App.initializing?
|
110
|
-
# We never report in the initialized because server is not yet ready until Karafka is
|
111
|
-
# fully running and some of the things like listeners are not yet available
|
112
|
-
return false if ::Karafka::App.initialized?
|
113
|
-
|
114
|
-
return true if forced
|
115
|
-
|
116
|
-
(monotonic_now - @tracked_at) >= ::Karafka::Web.config.tracking.interval
|
117
|
-
end
|
118
|
-
|
119
|
-
# @return [Object] sampler for the metrics
|
120
|
-
def consumer_sampler
|
121
|
-
@consumer_sampler ||= ::Karafka::Web.config.tracking.consumers.sampler
|
122
|
-
end
|
123
|
-
|
124
|
-
# Produces messages to Kafka.
|
125
|
-
#
|
126
|
-
# @param messages [Array<Hash>]
|
127
|
-
#
|
128
|
-
# @note We pick either sync or async dependent on number of messages. The trick here is,
|
129
|
-
# that we do not want to end up overloading the internal queue with messages in case
|
130
|
-
# someone has a lot of errors from processing or other errors. Producing sync will wait
|
131
|
-
# for the delivery, hence will slow things down a little bit. On the other hand during
|
132
|
-
# normal operations we should not have that many messages to dispatch and it should not
|
133
|
-
# slowdown any processing.
|
134
|
-
def produce(messages)
|
135
|
-
if messages.count >= PRODUCE_SYNC_THRESHOLD
|
136
|
-
::Karafka.producer.produce_many_sync(messages)
|
137
|
-
else
|
138
|
-
::Karafka.producer.produce_many_async(messages)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
@@ -1,81 +0,0 @@
|
|
1
|
-
<div id="summary" class="container text-center mb-5">
|
2
|
-
<div class="row">
|
3
|
-
|
4
|
-
<div class="col-lg-2 offset-md-1">
|
5
|
-
<div class="card mb-0">
|
6
|
-
<div class="card-body">
|
7
|
-
<p class="card-text">
|
8
|
-
<div class="stat mb-1">
|
9
|
-
<%= @counters.processes %>
|
10
|
-
</div>
|
11
|
-
<div class="desc">
|
12
|
-
Processes
|
13
|
-
</div>
|
14
|
-
</p>
|
15
|
-
</div>
|
16
|
-
</div>
|
17
|
-
</div>
|
18
|
-
|
19
|
-
<div class="col-lg-2">
|
20
|
-
<div class="card mb-0">
|
21
|
-
<div class="card-body">
|
22
|
-
<p class="card-text">
|
23
|
-
<div class="stat mb-1">
|
24
|
-
<%= @counters.threads_count %>
|
25
|
-
</div>
|
26
|
-
<div class="desc">
|
27
|
-
Worker threads
|
28
|
-
</div>
|
29
|
-
</p>
|
30
|
-
</div>
|
31
|
-
</div>
|
32
|
-
</div>
|
33
|
-
|
34
|
-
<div class="col-lg-2">
|
35
|
-
<div class="card mb-0">
|
36
|
-
<div class="card-body">
|
37
|
-
<p class="card-text">
|
38
|
-
<div class="stat mb-1">
|
39
|
-
<%= @counters.listeners_count %>
|
40
|
-
</div>
|
41
|
-
<div class="desc">
|
42
|
-
Listeners
|
43
|
-
</div>
|
44
|
-
</p>
|
45
|
-
</div>
|
46
|
-
</div>
|
47
|
-
</div>
|
48
|
-
|
49
|
-
<div class="col-lg-2">
|
50
|
-
<div class="card mb-0">
|
51
|
-
<div class="card-body">
|
52
|
-
<p class="card-text">
|
53
|
-
<div class="stat mb-1">
|
54
|
-
<%= @counters.utilization.round(2) %>
|
55
|
-
%
|
56
|
-
</div>
|
57
|
-
<div class="desc">
|
58
|
-
Utilization
|
59
|
-
</div>
|
60
|
-
</p>
|
61
|
-
</div>
|
62
|
-
</div>
|
63
|
-
</div>
|
64
|
-
|
65
|
-
<div class="col-lg-2">
|
66
|
-
<div class="card mb-0">
|
67
|
-
<div class="card-body">
|
68
|
-
<p class="card-text">
|
69
|
-
<div class="stat mb-1">
|
70
|
-
<%= format_memory @counters.rss %>
|
71
|
-
</div>
|
72
|
-
<div class="desc">
|
73
|
-
RSS
|
74
|
-
</div>
|
75
|
-
</p>
|
76
|
-
</div>
|
77
|
-
</div>
|
78
|
-
</div>
|
79
|
-
|
80
|
-
</div>
|
81
|
-
</div>
|
@@ -1,31 +0,0 @@
|
|
1
|
-
<% if v.is_a?(Hash) %>
|
2
|
-
<% v.each do |k2, v2| %>
|
3
|
-
<tr>
|
4
|
-
<td>
|
5
|
-
<%= "#{k}.#{k2}" %>
|
6
|
-
</td>
|
7
|
-
<td>
|
8
|
-
<% if %w[sasl ssl].any? { |scope| k2.to_s.include?(scope) } %>
|
9
|
-
***
|
10
|
-
<% elsif k2.to_s == 'tags' %>
|
11
|
-
<%== tags(v2) %>
|
12
|
-
<% else %>
|
13
|
-
<%= v2 %>
|
14
|
-
<% end %>
|
15
|
-
</td>
|
16
|
-
</tr>
|
17
|
-
<% end %>
|
18
|
-
<% else %>
|
19
|
-
<tr>
|
20
|
-
<td>
|
21
|
-
<%= k %>
|
22
|
-
</td>
|
23
|
-
<td>
|
24
|
-
<% if k == :occurred_at %>
|
25
|
-
<%== relative_time v %>
|
26
|
-
<% else %>
|
27
|
-
<%= v %>
|
28
|
-
<% end %>
|
29
|
-
</td>
|
30
|
-
</tr>
|
31
|
-
<% end %>
|
@@ -1,27 +0,0 @@
|
|
1
|
-
<% if @process %>
|
2
|
-
<li class="breadcrumb-item">
|
3
|
-
<a href="<%= root_path('consumers') %>">
|
4
|
-
Consumers
|
5
|
-
</a>
|
6
|
-
</li>
|
7
|
-
|
8
|
-
<li class="breadcrumb-item">
|
9
|
-
<a href="<%= root_path('consumers', @process.id, 'subscriptions') %>">
|
10
|
-
<%= @process.name %>
|
11
|
-
</a>
|
12
|
-
</li>
|
13
|
-
|
14
|
-
<% if current_path.include?('/jobs') %>
|
15
|
-
<li class="breadcrumb-item">
|
16
|
-
<a href="<%= root_path('consumers', @process.id, 'jobs') %>">
|
17
|
-
Running jobs
|
18
|
-
</a>
|
19
|
-
</li>
|
20
|
-
<% else %>
|
21
|
-
<li class="breadcrumb-item">
|
22
|
-
<a href="<%= root_path('consumers', @process.id, 'subscriptions') %>">
|
23
|
-
Active subscriptions
|
24
|
-
</a>
|
25
|
-
</li>
|
26
|
-
<% end %>
|
27
|
-
<% end %>
|