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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +69 -6
  4. data/Gemfile.lock +14 -14
  5. data/karafka-web.gemspec +3 -3
  6. data/lib/karafka/web/config.rb +11 -5
  7. data/lib/karafka/web/installer.rb +2 -3
  8. data/lib/karafka/web/tracking/consumers/contracts/consumer_group.rb +1 -1
  9. data/lib/karafka/web/tracking/consumers/contracts/job.rb +4 -1
  10. data/lib/karafka/web/tracking/consumers/contracts/partition.rb +1 -1
  11. data/lib/karafka/web/tracking/consumers/contracts/report.rb +8 -4
  12. data/lib/karafka/web/tracking/consumers/contracts/subscription_group.rb +1 -1
  13. data/lib/karafka/web/tracking/consumers/contracts/topic.rb +3 -1
  14. data/lib/karafka/web/tracking/consumers/listeners/base.rb +2 -2
  15. data/lib/karafka/web/tracking/consumers/listeners/errors.rb +8 -44
  16. data/lib/karafka/web/tracking/consumers/listeners/processing.rb +5 -0
  17. data/lib/karafka/web/tracking/consumers/reporter.rb +151 -0
  18. data/lib/karafka/web/tracking/consumers/sampler.rb +2 -1
  19. data/lib/karafka/web/tracking/contracts/base.rb +34 -0
  20. data/lib/karafka/web/tracking/contracts/error.rb +31 -0
  21. data/lib/karafka/web/tracking/helpers/error_info.rb +50 -0
  22. data/lib/karafka/web/tracking/memoized_shell.rb +1 -1
  23. data/lib/karafka/web/tracking/producers/listeners/base.rb +33 -0
  24. data/lib/karafka/web/tracking/producers/listeners/errors.rb +66 -0
  25. data/lib/karafka/web/tracking/producers/listeners/reporter.rb +21 -0
  26. data/lib/karafka/web/tracking/producers/reporter.rb +101 -0
  27. data/lib/karafka/web/tracking/producers/sampler.rb +42 -0
  28. data/lib/karafka/web/tracking/sampler.rb +5 -0
  29. data/lib/karafka/web/ui/controllers/consumers.rb +2 -4
  30. data/lib/karafka/web/ui/models/counters.rb +51 -0
  31. data/lib/karafka/web/ui/models/status.rb +31 -7
  32. data/lib/karafka/web/ui/pro/controllers/consumers.rb +2 -3
  33. data/lib/karafka/web/ui/pro/views/consumers/consumer/_job.erb +6 -6
  34. data/lib/karafka/web/ui/pro/views/consumers/consumer/_metrics.erb +6 -1
  35. data/lib/karafka/web/ui/pro/views/consumers/index.erb +25 -21
  36. data/lib/karafka/web/ui/pro/views/consumers/jobs.erb +1 -1
  37. data/lib/karafka/web/ui/pro/views/errors/_breadcrumbs.erb +1 -2
  38. data/lib/karafka/web/ui/pro/views/errors/_error.erb +8 -6
  39. data/lib/karafka/web/ui/pro/views/errors/show.erb +3 -2
  40. data/lib/karafka/web/ui/public/stylesheets/application.css +4 -0
  41. data/lib/karafka/web/ui/views/consumers/_no_consumers.erb +9 -0
  42. data/lib/karafka/web/ui/views/consumers/index.erb +24 -20
  43. data/lib/karafka/web/ui/views/errors/_breadcrumbs.erb +1 -2
  44. data/lib/karafka/web/ui/views/errors/_detail.erb +9 -1
  45. data/lib/karafka/web/ui/views/errors/_error.erb +8 -6
  46. data/lib/karafka/web/ui/views/errors/show.erb +50 -2
  47. data/lib/karafka/web/ui/views/shared/_feature_pro.erb +4 -0
  48. data/lib/karafka/web/ui/views/shared/_pagination.erb +8 -2
  49. data/lib/karafka/web/ui/views/shared/exceptions/pro_only.erb +0 -4
  50. data/lib/karafka/web/ui/views/status/failures/_initial_state.erb +1 -10
  51. data/lib/karafka/web/ui/views/status/info/_components.erb +6 -1
  52. data/lib/karafka/web/ui/views/status/show.erb +6 -1
  53. data/lib/karafka/web/ui/views/status/successes/_connection.erb +1 -0
  54. data/lib/karafka/web/ui/views/status/warnings/_connection.erb +11 -0
  55. data/lib/karafka/web/version.rb +1 -1
  56. data.tar.gz.sig +0 -0
  57. metadata +28 -16
  58. metadata.gz.sig +0 -0
  59. data/lib/karafka/web/tracking/base_contract.rb +0 -31
  60. data/lib/karafka/web/tracking/reporter.rb +0 -144
  61. data/lib/karafka/web/ui/pro/views/consumers/_summary.erb +0 -81
  62. data/lib/karafka/web/ui/pro/views/errors/_cleaned.erb +0 -3
  63. data/lib/karafka/web/ui/pro/views/errors/_detail.erb +0 -31
  64. data/lib/karafka/web/ui/pro/views/errors/_no_errors.erb +0 -3
  65. data/lib/karafka/web/ui/pro/views/jobs/_breadcrumbs.erb +0 -5
  66. 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,3 +0,0 @@
1
- <div class="alert alert-info" role="alert">
2
- This errors topic partition had all of its errored compacted and cleaned.
3
- </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,3 +0,0 @@
1
- <div class="alert alert-info" role="alert">
2
- There are no errors in this errors topic partition.
3
- </div>
@@ -1,5 +0,0 @@
1
- <li class="breadcrumb-item">
2
- <a href="<%= root_path('jobs') %>">
3
- Running jobs
4
- </a>
5
- </li>
@@ -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 %>