karafka-web 0.2.5 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +39 -0
  4. data/Gemfile.lock +3 -3
  5. data/karafka-web.gemspec +1 -1
  6. data/lib/karafka/web/installer.rb +7 -2
  7. data/lib/karafka/web/tracking/consumers/sampler.rb +9 -22
  8. data/lib/karafka/web/tracking/sampler.rb +57 -0
  9. data/lib/karafka/web/ui/controllers/errors.rb +2 -0
  10. data/lib/karafka/web/ui/controllers/status.rb +1 -0
  11. data/lib/karafka/web/ui/models/job.rb +0 -4
  12. data/lib/karafka/web/ui/models/message.rb +71 -14
  13. data/lib/karafka/web/ui/models/process.rb +0 -7
  14. data/lib/karafka/web/ui/models/watermark_offsets.rb +39 -0
  15. data/lib/karafka/web/ui/pro/controllers/errors.rb +2 -1
  16. data/lib/karafka/web/ui/pro/controllers/explorer.rb +3 -0
  17. data/lib/karafka/web/ui/pro/views/consumers/consumer/_metrics.erb +29 -21
  18. data/lib/karafka/web/ui/pro/views/consumers/consumer/_partition.erb +14 -2
  19. data/lib/karafka/web/ui/pro/views/errors/_cleaned.erb +3 -0
  20. data/lib/karafka/web/ui/pro/views/errors/_error.erb +33 -25
  21. data/lib/karafka/web/ui/pro/views/errors/_no_errors.erb +3 -0
  22. data/lib/karafka/web/ui/pro/views/errors/index.erb +29 -21
  23. data/lib/karafka/web/ui/pro/views/explorer/_message.erb +27 -16
  24. data/lib/karafka/web/ui/pro/views/explorer/partition/_cleaned.erb +3 -0
  25. data/lib/karafka/web/ui/pro/views/explorer/partition/_empty.erb +3 -0
  26. data/lib/karafka/web/ui/pro/views/explorer/partition/_messages.erb +18 -0
  27. data/lib/karafka/web/ui/pro/views/explorer/partition/_watermark_offsets.erb +10 -0
  28. data/lib/karafka/web/ui/pro/views/explorer/partition.erb +10 -19
  29. data/lib/karafka/web/ui/public/stylesheets/application.css +4 -0
  30. data/lib/karafka/web/ui/views/errors/_cleaned.erb +3 -0
  31. data/lib/karafka/web/ui/views/errors/_error.erb +33 -25
  32. data/lib/karafka/web/ui/views/errors/_no_errors.erb +3 -0
  33. data/lib/karafka/web/ui/views/errors/_watermark_offsets.erb +10 -0
  34. data/lib/karafka/web/ui/views/errors/index.erb +29 -21
  35. data/lib/karafka/web/ui/views/status/_info.erb +14 -0
  36. data/lib/karafka/web/ui/views/status/info/_components.erb +34 -0
  37. data/lib/karafka/web/ui/views/status/show.erb +9 -1
  38. data/lib/karafka/web/version.rb +1 -1
  39. data.tar.gz.sig +0 -0
  40. metadata +17 -4
  41. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f0eb0ab715c86da7538f8c83f3caa648f36663437ec1895c4a14fcd51d350d5
4
- data.tar.gz: c40f2936ea80c059e94c9d911ad8765859fd737d90d12a43490feb5b1b2c4282
3
+ metadata.gz: 1b6f97a725c2cd9e8458d8b4a32a347e42898e1f5598b3998a460e063fc0b149
4
+ data.tar.gz: 4a8762c41fa42f5e9c75380ce098423acd72db1720dbf412fd33cb412efc3855
5
5
  SHA512:
6
- metadata.gz: dcb9e83ad553937ff1de9c760d38e599de491a6866112c0ea55ba944d985026736392981c74e5c1d4b0e3c19157f6759b4deca8281c3a3ef5301724e51a79d6b
7
- data.tar.gz: aa56092d3ea42cb02880f4caa32a1e741a989ccdc9491e65c7d787dd68cd713758df3dc06475de217b277f6bcde794a67047c5107ca5d127834bf90b4007ad42
6
+ metadata.gz: b1899bee43d1597e59755ce9f148b817e2c69c3b1b76ee7c6058aef2fc4a985f057b50c26bd000cc121c3cf73c694f906a92a85885e2d5337630ed728ba3e1a1
7
+ data.tar.gz: aaca8f979aa93075cceb315f25be6ca98dcde650a6302cca3b4d134df7159210996c71010be28043cb673d0ec54a8a54f5c058ec3f83d30a3d343c230de1a4cb
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # Karafka Web changelog
2
2
 
3
+ ## 0.3.1 (2023-03-27)
4
+ - [Fix] Add missing retention policy for states topic.
5
+ - [Fix] Fix display of compacted messages placeholders for offsets lower than low watermark.
6
+ - [Fix] Fix invalid pagination per page count.
7
+
8
+ ### Upgrade notes
9
+
10
+ If upgrading from `0.3.0`, nothing.
11
+
12
+ If upgrading from lower, please follow `0.3.0` upgrade procedure.
13
+
14
+ ## 0.3.0 (2023-03-27)
15
+ - **[Feature]** Support paginating over compacted topics partitions.
16
+ - [Improvement] Display watermark offsets in the errors view.
17
+ - [Improvement] Display informative message when partition is empty due to a retention policy.
18
+ - [Improvement] Display informative message when partition is empty instead of displaying nothing.
19
+ - [Improvement] Display current watermark offsets in the Explorer when viewing list of messages from a given partition.
20
+ - [Improvement] Report extra debug info in the status section.
21
+ - [Improvement] Report not only `Karafka` and `WaterDrop` versions but also `Karafka::Core`, `Rdkafka` and `librdkafka` versions.
22
+ - [Improvement] Small CSS improvements.
23
+ - [Improvement] Provide nicer info when errors topic does not contain any errors or was compacted.
24
+ - [Improvement] Improve listing of errors including compacted once.
25
+ - [Fix] Fix pagination for compacted indexes that would display despite no data being available below the low watermark offset.
26
+ - [Fix] Fix a case where reading from a compacted offset would return no data despite data being available.
27
+ - [Fix] Fix a case where explorer pagination would suggest more pages for compacted topics.
28
+ - [Fix] Fix incorrect support of compacted partitions and partitions with low watermark offset other than 0.
29
+ - [Fix] Display `N/A` instead of `-1` and `-1001` on lag stored and stored offset for consumer processes that did not mark any messages as consumed yet in the per consumer view.
30
+ - [Maintenance] Remove compatibility fallbacks for job and process tags (#1342)
31
+ - [Maintenance] Extract base sampler for tracking and web.
32
+
33
+ ### Upgrade notes
34
+
35
+ Because of the removal of compatibility fallbacks for some metrics fetches, it is recommended to:
36
+
37
+ - First, deploy **all** the Karafka consumer processes (`karafka server`)
38
+ - Deploy the Web update to your web server.
39
+
40
+ Please note that if you decide to use the updated Web UI with not updated consumers, you may hit a 500 error.
41
+
3
42
  ## 0.2.5 (2023-03-17)
4
43
  - [Fix] Critical instrumentation async errors intercepted by Web don't have JID for job removal (#1366)
5
44
 
data/Gemfile.lock CHANGED
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka-web (0.2.5)
4
+ karafka-web (0.3.1)
5
5
  erubi (~> 1.4)
6
- karafka (>= 2.0.35, < 3.0.0)
6
+ karafka (>= 2.0.38, < 3.0.0)
7
7
  karafka-core (>= 2.0.12, < 3.0.0)
8
8
  roda (~> 3.63)
9
9
  tilt (~> 2.0)
@@ -26,7 +26,7 @@ GEM
26
26
  ffi (1.15.5)
27
27
  i18n (1.12.0)
28
28
  concurrent-ruby (~> 1.0)
29
- karafka (2.0.35)
29
+ karafka (2.0.38)
30
30
  karafka-core (>= 2.0.12, < 3.0.0)
31
31
  thor (>= 0.20)
32
32
  waterdrop (>= 2.4.10, < 3.0.0)
data/karafka-web.gemspec CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.licenses = %w[LGPL-3.0 Commercial]
18
18
 
19
19
  spec.add_dependency 'erubi', '~> 1.4'
20
- spec.add_dependency 'karafka', '>= 2.0.35', '< 3.0.0'
20
+ spec.add_dependency 'karafka', '>= 2.0.38', '< 3.0.0'
21
21
  spec.add_dependency 'karafka-core', '>= 2.0.12', '< 3.0.0'
22
22
  spec.add_dependency 'roda', '~> 3.63'
23
23
  spec.add_dependency 'tilt', '~> 2.0'
@@ -102,8 +102,13 @@ module Karafka
102
102
  consumers_states_topic,
103
103
  1,
104
104
  replication_factor,
105
- # We care only about the most recent state, previous are irrelevant
106
- { 'cleanup.policy': 'compact' }
105
+ # We care only about the most recent state, previous are irrelevant. So we can easily
106
+ # compact after one minute. We do not use this beyond the most recent collective
107
+ # state, hence it all can easily go away.
108
+ {
109
+ 'cleanup.policy': 'compact',
110
+ 'retention.ms': 60 * 60 * 1_000
111
+ }
107
112
  )
108
113
  end
109
114
 
@@ -6,7 +6,7 @@ module Karafka
6
6
  # Namespace for all the things related to tracking consumers and consuming processes
7
7
  module Consumers
8
8
  # Samples for fetching and storing metrics samples about the consumer process
9
- class Sampler
9
+ class Sampler < Tracking::Sampler
10
10
  include ::Karafka::Core::Helpers::Time
11
11
 
12
12
  attr_reader :counters, :consumer_groups, :errors, :times, :pauses, :jobs
@@ -14,7 +14,7 @@ module Karafka
14
14
  # Current schema version
15
15
  # This can be used in the future for detecting incompatible changes and writing
16
16
  # migrations
17
- SCHEMA_VERSION = '1.0.1'
17
+ SCHEMA_VERSION = '1.0.2'
18
18
 
19
19
  # 60 seconds window for time tracked window-based metrics
20
20
  TIMES_TTL = 60
@@ -25,6 +25,8 @@ module Karafka
25
25
  private_constant :TIMES_TTL, :TIMES_TTL_MS, :SCHEMA_VERSION
26
26
 
27
27
  def initialize
28
+ super
29
+
28
30
  @counters = {
29
31
  batches: 0,
30
32
  messages: 0,
@@ -72,8 +74,11 @@ module Karafka
72
74
 
73
75
  versions: {
74
76
  ruby: ruby_version,
75
- karafka: ::Karafka::VERSION,
76
- waterdrop: ::WaterDrop::VERSION
77
+ karafka: karafka_version,
78
+ waterdrop: waterdrop_version,
79
+ karafka_core: karafka_core_version,
80
+ rdkafka: rdkafka_version,
81
+ librdkafka: librdkafka_version
77
82
  },
78
83
 
79
84
  stats: jobs_queue_statistics.merge(
@@ -118,11 +123,6 @@ module Karafka
118
123
  Karafka::Server.listeners&.count.to_i
119
124
  end
120
125
 
121
- # @return [String] Unique process name
122
- def process_name
123
- @process_name ||= "#{Socket.gethostname}:#{::Process.pid}:#{SecureRandom.hex(6)}"
124
- end
125
-
126
126
  # @return [Integer] memory used by this process in kilobytes
127
127
  def memory_usage
128
128
  pid = ::Process.pid
@@ -210,19 +210,6 @@ module Karafka
210
210
  @cpu_count ||= Etc.nprocessors
211
211
  end
212
212
 
213
- # @return [String] currently used ruby version with details
214
- def ruby_version
215
- if defined?(JRUBY_VERSION)
216
- revision = JRUBY_REVISION.to_s
217
- version = JRUBY_VERSION
218
- else
219
- revision = RUBY_REVISION.to_s
220
- version = RUBY_ENGINE_VERSION
221
- end
222
-
223
- "#{RUBY_ENGINE} #{version}-#{RUBY_PATCHLEVEL} #{revision[0..5]}"
224
- end
225
-
226
213
  # @return [Integer] number of threads that process work
227
214
  def concurrency
228
215
  @concurrency ||= Karafka::App.config.concurrency
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ module Tracking
6
+ # Base sampler with some basic info collectors
7
+ # This sampler should store **only** collectors that can be used for producers, consumers and
8
+ # the Web-UI itself. All specific to a given aspect of operations should be moved out.
9
+ class Sampler
10
+ # @return [String] Unique process name
11
+ def process_name
12
+ @process_name ||= "#{Socket.gethostname}:#{::Process.pid}:#{SecureRandom.hex(6)}"
13
+ end
14
+
15
+ # @return [String] currently used ruby version with details
16
+ def ruby_version
17
+ return @ruby_version if @ruby_version
18
+
19
+ if defined?(JRUBY_VERSION)
20
+ revision = JRUBY_REVISION.to_s
21
+ version = JRUBY_VERSION
22
+ else
23
+ revision = RUBY_REVISION.to_s
24
+ version = RUBY_ENGINE_VERSION
25
+ end
26
+
27
+ @ruby_version = "#{RUBY_ENGINE} #{version}-#{RUBY_PATCHLEVEL} #{revision[0..5]}"
28
+ end
29
+
30
+ # @return [String] Karafka version
31
+ def karafka_version
32
+ ::Karafka::VERSION
33
+ end
34
+
35
+ # @return [String] Karafka::Core version
36
+ def karafka_core_version
37
+ ::Karafka::Core::VERSION
38
+ end
39
+
40
+ # @return [String] rdkafka version
41
+ def rdkafka_version
42
+ ::Rdkafka::VERSION
43
+ end
44
+
45
+ # @return [String] librdkafka version
46
+ def librdkafka_version
47
+ ::Rdkafka::LIBRDKAFKA_VERSION
48
+ end
49
+
50
+ # @return [String] WaterDrop version
51
+ def waterdrop_version
52
+ ::WaterDrop::VERSION
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -16,6 +16,8 @@ module Karafka
16
16
  @params.current_page
17
17
  )
18
18
 
19
+ @watermark_offsets = Ui::Models::WatermarkOffsets.find(errors_topic, 0)
20
+
19
21
  respond
20
22
  end
21
23
 
@@ -13,6 +13,7 @@ module Karafka
13
13
  # Displays the Web UI setup status
14
14
  def show
15
15
  @status = Models::Status.new
16
+ @sampler = Tracking::Sampler.new
16
17
 
17
18
  respond
18
19
  end
@@ -6,10 +6,6 @@ module Karafka
6
6
  module Models
7
7
  # Single job data representation model
8
8
  class Job < Lib::HashProxy
9
- # @return [Array<String>] tags of this consuming job / consumer
10
- def tags
11
- @hash[:tags] || []
12
- end
13
9
  end
14
10
  end
15
11
  end
@@ -38,43 +38,83 @@ module Karafka
38
38
  # @return [Array] We return both page data as well as all the details needed to build
39
39
  # the pagination details.
40
40
  def page(topic_id, partition_id, page)
41
- # Establish the leading offset
42
- lead = Karafka::Admin.read_topic(topic_id, partition_id, 1).first
41
+ low_offset, high_offset = Karafka::Admin.read_watermark_offsets(
42
+ topic_id,
43
+ partition_id
44
+ )
43
45
 
44
46
  partitions_count = fetch_partition_count(topic_id)
45
47
 
48
+ no_data_result = [false, [], false, partitions_count]
49
+
46
50
  # If there is not even one message, we need to early exit
47
- return [false, [], false, partitions_count] unless lead
51
+ # If low and high watermark offsets are of the same value, it means no data in the
52
+ # topic is present
53
+ return no_data_result if low_offset == high_offset
48
54
 
49
55
  # We add plus one because we compute previous offset from which we want to start and
50
56
  # not previous page leading offset
51
- previous_offset = lead.offset - (per_page * page) + 1
57
+ start_offset = high_offset - (per_page * page)
52
58
 
53
- if previous_offset.negative?
54
- count = per_page + previous_offset
59
+ if start_offset <= low_offset
60
+ count = per_page - (low_offset - start_offset)
55
61
  previous_page = page < 2 ? false : page - 1
56
62
  next_page = false
57
- previous_offset = 0
63
+ start_offset = low_offset
58
64
  else
59
65
  previous_page = page < 2 ? false : page - 1
60
66
  next_page = page + 1
61
67
  count = per_page
62
68
  end
63
69
 
64
- [
65
- previous_page,
66
- read_topic(topic_id, partition_id, count, previous_offset).reverse,
67
- next_page,
68
- partitions_count
69
- ]
70
+ # This code is a bit tricky. Since topics can be compacted and certain offsets may
71
+ # not be present at all, it may happen that we want to read from a non-existing
72
+ # offset. In case like this we need to catch this error (we do it in `read_topic`)
73
+ # and we need to move to an offset closer to high offset. This is not fast but it is
74
+ # an edge case that should not happen often when inspecting real time data. This can
75
+ # happen more often for heavily compacted topics with short retention but even then
76
+ # it is ok for 25 elements we usually operate on a single page.
77
+ count.times do |index|
78
+ context_offset = start_offset + index
79
+ # We need to get less if we move up with missing offsets to get exactly
80
+ # the number we needed
81
+ context_count = count - index
82
+
83
+ messages = read_topic(
84
+ topic_id,
85
+ partition_id,
86
+ context_count,
87
+ context_offset,
88
+ # We do not reset the offset here because we are not interested in seeking from
89
+ # any offset. We are interested in the indication, that there is no offset of a
90
+ # given value so we can try with a more recent one
91
+ 'auto.offset.reset': 'error'
92
+ )
93
+
94
+ next unless messages
95
+
96
+ return [
97
+ previous_page,
98
+ fill_compacted(messages, context_offset, context_count).reverse,
99
+ next_page,
100
+ partitions_count
101
+ ]
102
+ end
103
+
104
+ no_data_result
70
105
  end
71
106
 
72
107
  private
73
108
 
74
109
  # @param args [Object] anything required by the admin `#read_topic`
75
- # @return [Array<Karafka::Messages::Message>] topic partition messages
110
+ # @return [Array<Karafka::Messages::Message>, false] topic partition messages or false
111
+ # in case we hit a non-existing offset
76
112
  def read_topic(*args)
77
113
  ::Karafka::Admin.read_topic(*args)
114
+ rescue Rdkafka::RdkafkaError => e
115
+ return false if e.code == :auto_offset_reset
116
+
117
+ raise
78
118
  end
79
119
 
80
120
  # @param topic_id [String] id of the topic
@@ -91,6 +131,23 @@ module Karafka
91
131
  def per_page
92
132
  ::Karafka::Web.config.ui.per_page
93
133
  end
134
+
135
+ # Since we paginate with compacted offsets visible but we do not get compacted messages
136
+ # we need to fill those with just the missing offset and handle this on the UI.
137
+ #
138
+ # @param messages [Array<Karafka::Messages::Message>] selected messages
139
+ # @param start_offset [Integer] offset of the first message (lowest) that we received
140
+ # @param count [Integer] how many messages we wanted - we need that to fill spots to
141
+ # have exactly the number that was requested and not more
142
+ # @return [Array<Karafka::Messages::Message, Integer>] array with gaps filled with the
143
+ # missing offset
144
+ def fill_compacted(messages, start_offset, count)
145
+ Array.new(count) do |index|
146
+ messages.find do |message|
147
+ (message.offset - start_offset) == index
148
+ end || start_offset + index
149
+ end
150
+ end
94
151
  end
95
152
  end
96
153
  end
@@ -40,13 +40,6 @@ module Karafka
40
40
  .sort_by(&:started_at)
41
41
  end
42
42
 
43
- # @return [Array<String>] tags of this consuming process
44
- # @note We use direct reference here and a fallback for users that come from the versions
45
- # that did not report tags
46
- def tags
47
- @hash[:process][:tags] || []
48
- end
49
-
50
43
  # @return [Integer] collective lag on this process
51
44
  def lag_stored
52
45
  consumer_groups
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ module Ui
6
+ module Models
7
+ # Model used for accessing watermark offsets
8
+ class WatermarkOffsets < Lib::HashProxy
9
+ class << self
10
+ # Retrieve watermark offsets for given topic partition
11
+ #
12
+ # @param topic_id [String]
13
+ # @param partition_id [Integer]
14
+ # @return [WatermarkOffsets]
15
+ def find(topic_id, partition_id)
16
+ offsets = ::Karafka::Admin.read_watermark_offsets(topic_id, partition_id)
17
+
18
+ new(
19
+ low: offsets.first,
20
+ high: offsets.last
21
+ )
22
+ end
23
+ end
24
+
25
+ # @return [Boolean] true if given partition never had any messages and is empty
26
+ def empty?
27
+ low.zero? && high.zero?
28
+ end
29
+
30
+ # @return [Boolean] true if given partition had data but all of it was removed due
31
+ # to log retention and compaction policies
32
+ def cleaned?
33
+ !empty? && low == high
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -29,6 +29,8 @@ module Karafka
29
29
  @params.current_page
30
30
  )
31
31
 
32
+ @watermark_offsets = Ui::Models::WatermarkOffsets.find(errors_topic, @partition_id)
33
+
32
34
  respond
33
35
  end
34
36
 
@@ -38,7 +40,6 @@ module Karafka
38
40
  # @param offset [Integer]
39
41
  def show(partition_id, offset)
40
42
  errors_topic = ::Karafka::Web.config.topics.errors
41
-
42
43
  @partition_id = partition_id
43
44
  @offset = offset
44
45
  @error_message = Models::Message.find(
@@ -36,6 +36,9 @@ module Karafka
36
36
  def partition(topic_id, partition_id)
37
37
  @topic_id = topic_id
38
38
  @partition_id = partition_id
39
+
40
+ @watermark_offsets = Ui::Models::WatermarkOffsets.find(topic_id, partition_id)
41
+
39
42
  @previous_page, @messages, @next_page, @partitions_count = Ui::Models::Message.page(
40
43
  @topic_id,
41
44
  @partition_id,
@@ -102,27 +102,35 @@
102
102
  <div class="card">
103
103
  <div class="card-body">
104
104
  <h5 class="card-title">Versions</h5>
105
- <p class="card-text">
106
- <ul style="list-style: square !important;">
107
- <li class="align-items-center d-flex justify-content-between">
108
- Ruby:
109
- <span class="badge bg-secondary">
110
- <%= @process.ruby %>
111
- </span>
112
- </li>
113
- <li class="align-items-center d-flex justify-content-between">
114
- Karafka:
115
- <span class="badge bg-secondary">
116
- <%= @process.karafka %>
117
- </span>
118
- </li>
119
- <li class="align-items-center d-flex justify-content-between">
120
- WaterDrop version:
121
- <span class="badge bg-secondary">
122
- <%= @process.waterdrop %>
123
- </span>
124
- </li>
125
- </ul>
105
+ <p class="card-text text-center">
106
+ <span class="badge bg-secondary">
107
+ <%= @process.ruby %>
108
+ </span>
109
+
110
+ <span class="badge bg-secondary">
111
+ karafka
112
+ <%= @process.karafka %>
113
+ </span>
114
+
115
+ <span class="badge bg-secondary">
116
+ karafka core
117
+ <%= @process.karafka_core %>
118
+ </span>
119
+
120
+ <span class="badge bg-secondary">
121
+ rdkafka
122
+ <%= @process.rdkafka %>
123
+ </span>
124
+
125
+ <span class="badge bg-secondary">
126
+ librdkafka
127
+ <%= @process.librdkafka %>
128
+ </span>
129
+
130
+ <span class="badge bg-secondary">
131
+ waterdrop
132
+ <%= @process.waterdrop %>
133
+ </span>
126
134
  </p>
127
135
  </div>
128
136
  </div>
@@ -6,7 +6,13 @@
6
6
  <%= partition.id %>
7
7
  </td>
8
8
  <td>
9
- <%= partition.lag_stored %>
9
+ <% if partition.lag_stored.to_i < 0 %>
10
+ <span class="badge bg-secondary" title="Not available until first offset commit">
11
+ N/A
12
+ </span>
13
+ <% else %>
14
+ <%= partition.lag_stored %>
15
+ <% end %>
10
16
  </td>
11
17
  <td>
12
18
  <span class="badge <%= lag_trend_bg(partition.lag_stored_d) %>">
@@ -17,7 +23,13 @@
17
23
  <%= partition.committed_offset %>
18
24
  </td>
19
25
  <td>
20
- <%= partition.stored_offset %>
26
+ <% if partition.stored_offset.to_i < 0 %>
27
+ <span class="badge bg-secondary" title="Not available until first offset commit">
28
+ N/A
29
+ </span>
30
+ <% else %>
31
+ <%= partition.stored_offset %>
32
+ <% end %>
21
33
  </td>
22
34
  <td>
23
35
  <span class="badge <%= kafka_state_bg(partition.fetch_state) %> mt-1 mb-1">
@@ -0,0 +1,3 @@
1
+ <div class="alert alert-info" role="alert">
2
+ This errors topic partition had all of its errored compacted and cleaned.
3
+ </div>
@@ -1,26 +1,34 @@
1
- <% error = error_msg.payload %>
1
+ <% if error_msg.is_a?(Integer) %>
2
+ <tr>
3
+ <td colspan="5" class="text-center text-muted">
4
+ This error has either been removed or compacted and is no longer available.
5
+ </td>
6
+ </tr>
7
+ <% else %>
8
+ <% error = error_msg.payload %>
2
9
 
3
- <tr>
4
- <td>
5
- <% if error[:details].key?(:topic) %>
6
- <%= error[:details][:topic] %>: <%= error[:details][:partition] %>
7
- <% else %>
8
- <%= error[:type] %>
9
- <% end %>
10
- </td>
11
- <td>
12
- <%== error[:process_name] %>
13
- </td>
14
- <td>
15
- <%= error[:error_class] %>:
16
- <%= error[:error_message].first(200) %>
17
- </td>
18
- <td>
19
- <%== relative_time error[:occurred_at] %>
20
- </td>
21
- <td>
22
- <a href="<%= root_path('errors', error_msg.partition, error_msg.offset) %>" class="btn btn-sm btn-secondary text-white">
23
- Details
24
- </a>
25
- </td>
26
- </tr>
10
+ <tr>
11
+ <td>
12
+ <% if error[:details].key?(:topic) %>
13
+ <%= error[:details][:topic] %>: <%= error[:details][:partition] %>
14
+ <% else %>
15
+ <%= error[:type] %>
16
+ <% end %>
17
+ </td>
18
+ <td>
19
+ <%== error[:process_name] %>
20
+ </td>
21
+ <td>
22
+ <%= error[:error_class] %>:
23
+ <%= error[:error_message].first(200) %>
24
+ </td>
25
+ <td>
26
+ <%== relative_time error[:occurred_at] %>
27
+ </td>
28
+ <td class="text-center">
29
+ <a href="<%= root_path('errors', error_msg.partition, error_msg.offset) %>" class="btn btn-sm btn-secondary text-white">
30
+ Details
31
+ </a>
32
+ </td>
33
+ </tr>
34
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <div class="alert alert-info" role="alert">
2
+ There are no errors in this errors topic partition.
3
+ </div>
@@ -1,4 +1,4 @@
1
- <div class="container mb-5">
1
+ <div class="container mb-4">
2
2
  <div class="row">
3
3
  <div class="col">
4
4
  <h3>
@@ -33,26 +33,34 @@
33
33
  <div class="container">
34
34
  <div class="row mb-5">
35
35
  <div class="col-sm-12">
36
- <table class="processes bg-white table table-hover table-bordered table-striped mb-0 align-middle">
37
- <thead>
38
- <tr class="align-middle">
39
- <th>Origin</th>
40
- <th>Process name</th>
41
- <th>Error</th>
42
- <th>Occurred at</th>
43
- <th></th>
44
- </tr>
45
- </thead>
46
- <tbody>
47
- <%==
48
- each_partial(
49
- @error_messages,
50
- 'errors/error',
51
- local: :error_msg
52
- )
53
- %>
54
- </tbody>
55
- </table>
36
+ <%== partial('explorer/partition/watermark_offsets') %>
37
+
38
+ <% if @watermark_offsets.empty? %>
39
+ <%== partial 'errors/no_errors' %>
40
+ <% elsif @watermark_offsets.cleaned? %>
41
+ <%== partial 'errors/cleaned' %>
42
+ <% else %>
43
+ <table class="processes bg-white table table-hover table-bordered table-striped mb-0 align-middle">
44
+ <thead>
45
+ <tr class="align-middle">
46
+ <th>Origin</th>
47
+ <th>Process name</th>
48
+ <th>Error</th>
49
+ <th>Occurred at</th>
50
+ <th></th>
51
+ </tr>
52
+ </thead>
53
+ <tbody>
54
+ <%==
55
+ each_partial(
56
+ @error_messages,
57
+ 'errors/error',
58
+ local: :error_msg
59
+ )
60
+ %>
61
+ </tbody>
62
+ </table>
63
+ <% end %>
56
64
  </div>
57
65
  </div>
58
66
  </div>
@@ -1,16 +1,27 @@
1
- <tr>
2
- <td>
3
- <%= message.offset %>
4
- </td>
5
- <td>
6
- <%== relative_time message.timestamp %>
7
- </td>
8
- <td>
9
- <%= message.key %>
10
- </td>
11
- <td class="text-center">
12
- <a href="<%= root_path('explorer', message.topic, message.partition, message.offset) %>" class="btn btn-sm btn-secondary">
13
- Details
14
- </a>
15
- </td>
16
- </tr>
1
+ <% if message.is_a?(Integer) %>
2
+ <tr>
3
+ <td class="text-muted">
4
+ <%= message %>
5
+ </td>
6
+ <td colspan="3" class="text-center text-muted">
7
+ This message has either been removed or compacted and is no longer available.
8
+ </td>
9
+ </tr>
10
+ <% else %>
11
+ <tr>
12
+ <td>
13
+ <%= message.offset %>
14
+ </td>
15
+ <td>
16
+ <%== relative_time message.timestamp %>
17
+ </td>
18
+ <td>
19
+ <%= message.key %>
20
+ </td>
21
+ <td class="text-center">
22
+ <a href="<%= root_path('explorer', message.topic, message.partition, message.offset) %>" class="btn btn-sm btn-secondary">
23
+ Details
24
+ </a>
25
+ </td>
26
+ </tr>
27
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <div class="alert alert-info mt-4" role="alert">
2
+ This partition no longer contains any data and has been fully compacted.
3
+ </div>
@@ -0,0 +1,3 @@
1
+ <div class="alert alert-info mt-4" role="alert">
2
+ This partition is empty and does not contain any data.
3
+ </div>
@@ -0,0 +1,18 @@
1
+ <table class="processes bg-white table table-hover table-bordered table-striped mb-0 align-middle">
2
+ <thead>
3
+ <tr class="align-middle">
4
+ <th>Offset</th>
5
+ <th>Timestamp</th>
6
+ <th>Key</th>
7
+ <th></th>
8
+ </tr>
9
+ </thead>
10
+ <tbody>
11
+ <%==
12
+ each_partial(
13
+ @messages,
14
+ 'explorer/message'
15
+ )
16
+ %>
17
+ </tbody>
18
+ </table>
@@ -0,0 +1,10 @@
1
+ <p class="text-end mb-4">
2
+ Watermark offsets:
3
+ <span class="badge bg-secondary mt-1 mb-1">
4
+ high: <%= @watermark_offsets.high %>
5
+ </span>
6
+
7
+ <span class="badge bg-secondary mt-1 mb-1">
8
+ low: <%= @watermark_offsets.low %>
9
+ </span>
10
+ </p>
@@ -1,4 +1,4 @@
1
- <div class="container mb-5">
1
+ <div class="container mb-4">
2
2
  <div class="row">
3
3
  <div class="col">
4
4
  <h3>
@@ -33,24 +33,15 @@
33
33
  <div class="container">
34
34
  <div class="row mb-5">
35
35
  <div class="col-sm-12">
36
- <table class="processes bg-white table table-hover table-bordered table-striped mb-0 align-middle">
37
- <thead>
38
- <tr class="align-middle">
39
- <th>Offset</th>
40
- <th>Timestamp</th>
41
- <th>Key</th>
42
- <th></th>
43
- </tr>
44
- </thead>
45
- <tbody>
46
- <%==
47
- each_partial(
48
- @messages,
49
- 'explorer/message'
50
- )
51
- %>
52
- </tbody>
53
- </table>
36
+ <%== partial('explorer/partition/watermark_offsets') %>
37
+
38
+ <% if @watermark_offsets.empty? %>
39
+ <%== partial 'explorer/partition/empty' %>
40
+ <% elsif @watermark_offsets.cleaned? %>
41
+ <%== partial 'explorer/partition/cleaned' %>
42
+ <% else %>
43
+ <%== partial('explorer/partition/messages') %>
44
+ <% end %>
54
45
  </div>
55
46
  </div>
56
47
  </div>
@@ -104,3 +104,7 @@ code.wrapped {
104
104
  #content .error-message {
105
105
  max-width: 700px;
106
106
  }
107
+
108
+ #metrics ul {
109
+ padding-left: 0;
110
+ }
@@ -0,0 +1,3 @@
1
+ <div class="alert alert-info" role="alert">
2
+ This errors topic partition had all of its errored compacted and cleaned.
3
+ </div>
@@ -1,26 +1,34 @@
1
- <% error = error_msg.payload %>
1
+ <% if error_msg.is_a?(Integer) %>
2
+ <tr>
3
+ <td colspan="5" class="text-center text-muted">
4
+ This error has either been removed or compacted and is no longer available.
5
+ </td>
6
+ </tr>
7
+ <% else %>
8
+ <% error = error_msg.payload %>
2
9
 
3
- <tr>
4
- <td>
5
- <% if error[:details].key?(:topic) %>
6
- <%= error[:details][:topic] %>: <%= error[:details][:partition] %>
7
- <% else %>
8
- <%= error[:type] %>
9
- <% end %>
10
- </td>
11
- <td>
12
- <%== error[:process_name] %>
13
- </td>
14
- <td>
15
- <%= error[:error_class] %>:
16
- <%= error[:error_message].first(200) %>
17
- </td>
18
- <td>
19
- <%== relative_time error[:occurred_at] %>
20
- </td>
21
- <td>
22
- <a href="<%= root_path('errors', error_msg.offset) %>" class="btn btn-sm btn-secondary text-white">
23
- Details
24
- </a>
25
- </td>
26
- </tr>
10
+ <tr>
11
+ <td>
12
+ <% if error[:details].key?(:topic) %>
13
+ <%= error[:details][:topic] %>: <%= error[:details][:partition] %>
14
+ <% else %>
15
+ <%= error[:type] %>
16
+ <% end %>
17
+ </td>
18
+ <td>
19
+ <%== error[:process_name] %>
20
+ </td>
21
+ <td>
22
+ <%= error[:error_class] %>:
23
+ <%= error[:error_message].first(200) %>
24
+ </td>
25
+ <td>
26
+ <%== relative_time error[:occurred_at] %>
27
+ </td>
28
+ <td class="text-center">
29
+ <a href="<%= root_path('errors', error_msg.offset) %>" class="btn btn-sm btn-secondary text-white">
30
+ Details
31
+ </a>
32
+ </td>
33
+ </tr>
34
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <div class="alert alert-info" role="alert">
2
+ There are no errors in this errors topic partition.
3
+ </div>
@@ -0,0 +1,10 @@
1
+ <p class="text-end mb-4">
2
+ Watermark offsets:
3
+ <span class="badge bg-secondary mt-1 mb-1">
4
+ high: <%= @watermark_offsets.high %>
5
+ </span>
6
+
7
+ <span class="badge bg-secondary mt-1 mb-1">
8
+ low: <%= @watermark_offsets.low %>
9
+ </span>
10
+ </p>
@@ -1,4 +1,4 @@
1
- <div class="container mb-5">
1
+ <div class="container mb-4">
2
2
  <div class="row">
3
3
  <div class="col">
4
4
  <h3>
@@ -13,26 +13,34 @@
13
13
  <div class="container">
14
14
  <div class="row mb-5">
15
15
  <div class="col-sm-12">
16
- <table class="processes bg-white table table-hover table-bordered table-striped mb-0 align-middle">
17
- <thead>
18
- <tr class="align-middle">
19
- <th>Origin</th>
20
- <th>Process name</th>
21
- <th>Error</th>
22
- <th>Occurred at</th>
23
- <th></th>
24
- </tr>
25
- </thead>
26
- <tbody>
27
- <%==
28
- each_partial(
29
- @error_messages,
30
- 'errors/error',
31
- local: :error_msg
32
- )
33
- %>
34
- </tbody>
35
- </table>
16
+ <%== partial('errors/watermark_offsets') %>
17
+
18
+ <% if @watermark_offsets.empty? %>
19
+ <%== partial 'errors/no_errors' %>
20
+ <% elsif @watermark_offsets.cleaned? %>
21
+ <%== partial 'errors/cleaned' %>
22
+ <% else %>
23
+ <table class="processes bg-white table table-hover table-bordered table-striped mb-0 align-middle">
24
+ <thead>
25
+ <tr class="align-middle">
26
+ <th>Origin</th>
27
+ <th>Process name</th>
28
+ <th>Error</th>
29
+ <th>Occurred at</th>
30
+ <th></th>
31
+ </tr>
32
+ </thead>
33
+ <tbody>
34
+ <%==
35
+ each_partial(
36
+ @error_messages,
37
+ 'errors/error',
38
+ local: :error_msg
39
+ )
40
+ %>
41
+ </tbody>
42
+ </table>
43
+ <% end %>
36
44
  </div>
37
45
  </div>
38
46
  </div>
@@ -0,0 +1,14 @@
1
+ <div class="card text-bg-alt mb-3">
2
+ <div class="card-header">
3
+ <span>
4
+ <%= title %>
5
+ </span>
6
+
7
+ <span class="float-end">
8
+ <span class="badge text-bg-secondary">Info</span>
9
+ </span>
10
+ </div>
11
+ <div class="card-body">
12
+ <%== description %>
13
+ </div>
14
+ </div>
@@ -0,0 +1,34 @@
1
+ <p>
2
+ Below you can find information about your setup. This can be helpful when debugging or reporting bugs and issues.
3
+ </p>
4
+
5
+ <p class="mb-0">
6
+ <span class="badge bg-secondary">
7
+ <%= @sampler.ruby_version %>
8
+ </span>
9
+
10
+ <span class="badge bg-secondary">
11
+ karafka
12
+ <%= @sampler.karafka_version %>
13
+ </span>
14
+
15
+ <span class="badge bg-secondary">
16
+ karafka core
17
+ <%= @sampler.karafka_core_version %>
18
+ </span>
19
+
20
+ <span class="badge bg-secondary">
21
+ rdkafka
22
+ <%= @sampler.rdkafka_version %>
23
+ </span>
24
+
25
+ <span class="badge bg-secondary">
26
+ librdkafka
27
+ <%= @sampler.librdkafka_version %>
28
+ </span>
29
+
30
+ <span class="badge bg-secondary">
31
+ waterdrop
32
+ <%= @sampler.waterdrop_version %>
33
+ </span>
34
+ </p>
@@ -3,7 +3,6 @@
3
3
  <div class="container mb-5">
4
4
  <div class="row">
5
5
  <div class="col-lg-10 offset-md-1">
6
-
7
6
  <%==
8
7
  partial(
9
8
  "status/#{@status.connection.to_s}",
@@ -104,6 +103,15 @@
104
103
  )
105
104
  %>
106
105
 
106
+ <%==
107
+ partial(
108
+ 'status/info',
109
+ locals: {
110
+ title: 'Components info',
111
+ description: partial('status/info/components')
112
+ }
113
+ )
114
+ %>
107
115
  </div>
108
116
  </div>
109
117
  </div>
@@ -3,6 +3,6 @@
3
3
  module Karafka
4
4
  module Web
5
5
  # Current gem version
6
- VERSION = '0.2.5'
6
+ VERSION = '0.3.1'
7
7
  end
8
8
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: karafka-web
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Mensfeld
@@ -35,7 +35,7 @@ cert_chain:
35
35
  Qf04B9ceLUaC4fPVEz10FyobjaFoY4i32xRto3XnrzeAgfEe4swLq8bQsR3w/EF3
36
36
  MGU0FeSV2Yj7Xc2x/7BzLK8xQn5l7Yy75iPF+KP3vVmDHnNl
37
37
  -----END CERTIFICATE-----
38
- date: 2023-03-17 00:00:00.000000000 Z
38
+ date: 2023-03-27 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: erubi
@@ -57,7 +57,7 @@ dependencies:
57
57
  requirements:
58
58
  - - ">="
59
59
  - !ruby/object:Gem::Version
60
- version: 2.0.35
60
+ version: 2.0.38
61
61
  - - "<"
62
62
  - !ruby/object:Gem::Version
63
63
  version: 3.0.0
@@ -67,7 +67,7 @@ dependencies:
67
67
  requirements:
68
68
  - - ">="
69
69
  - !ruby/object:Gem::Version
70
- version: 2.0.35
70
+ version: 2.0.38
71
71
  - - "<"
72
72
  - !ruby/object:Gem::Version
73
73
  version: 3.0.0
@@ -186,6 +186,7 @@ files:
186
186
  - lib/karafka/web/tracking/consumers/sampler.rb
187
187
  - lib/karafka/web/tracking/memoized_shell.rb
188
188
  - lib/karafka/web/tracking/reporter.rb
189
+ - lib/karafka/web/tracking/sampler.rb
189
190
  - lib/karafka/web/tracking/ttl_array.rb
190
191
  - lib/karafka/web/tracking/ttl_hash.rb
191
192
  - lib/karafka/web/ui/app.rb
@@ -213,6 +214,7 @@ files:
213
214
  - lib/karafka/web/ui/models/state.rb
214
215
  - lib/karafka/web/ui/models/status.rb
215
216
  - lib/karafka/web/ui/models/topic.rb
217
+ - lib/karafka/web/ui/models/watermark_offsets.rb
216
218
  - lib/karafka/web/ui/pro/app.rb
217
219
  - lib/karafka/web/ui/pro/controllers/cluster.rb
218
220
  - lib/karafka/web/ui/pro/controllers/consumers.rb
@@ -243,8 +245,10 @@ files:
243
245
  - lib/karafka/web/ui/pro/views/dlq/_topic.erb
244
246
  - lib/karafka/web/ui/pro/views/dlq/index.erb
245
247
  - lib/karafka/web/ui/pro/views/errors/_breadcrumbs.erb
248
+ - lib/karafka/web/ui/pro/views/errors/_cleaned.erb
246
249
  - lib/karafka/web/ui/pro/views/errors/_detail.erb
247
250
  - lib/karafka/web/ui/pro/views/errors/_error.erb
251
+ - lib/karafka/web/ui/pro/views/errors/_no_errors.erb
248
252
  - lib/karafka/web/ui/pro/views/errors/_partition_option.erb
249
253
  - lib/karafka/web/ui/pro/views/errors/index.erb
250
254
  - lib/karafka/web/ui/pro/views/errors/show.erb
@@ -257,6 +261,10 @@ files:
257
261
  - lib/karafka/web/ui/pro/views/explorer/_topic.erb
258
262
  - lib/karafka/web/ui/pro/views/explorer/index.erb
259
263
  - lib/karafka/web/ui/pro/views/explorer/partition.erb
264
+ - lib/karafka/web/ui/pro/views/explorer/partition/_cleaned.erb
265
+ - lib/karafka/web/ui/pro/views/explorer/partition/_empty.erb
266
+ - lib/karafka/web/ui/pro/views/explorer/partition/_messages.erb
267
+ - lib/karafka/web/ui/pro/views/explorer/partition/_watermark_offsets.erb
260
268
  - lib/karafka/web/ui/pro/views/explorer/show.erb
261
269
  - lib/karafka/web/ui/pro/views/health/_breadcrumbs.erb
262
270
  - lib/karafka/web/ui/pro/views/health/_partition.erb
@@ -287,8 +295,11 @@ files:
287
295
  - lib/karafka/web/ui/views/consumers/_summary.erb
288
296
  - lib/karafka/web/ui/views/consumers/index.erb
289
297
  - lib/karafka/web/ui/views/errors/_breadcrumbs.erb
298
+ - lib/karafka/web/ui/views/errors/_cleaned.erb
290
299
  - lib/karafka/web/ui/views/errors/_detail.erb
291
300
  - lib/karafka/web/ui/views/errors/_error.erb
301
+ - lib/karafka/web/ui/views/errors/_no_errors.erb
302
+ - lib/karafka/web/ui/views/errors/_watermark_offsets.erb
292
303
  - lib/karafka/web/ui/views/errors/index.erb
293
304
  - lib/karafka/web/ui/views/errors/show.erb
294
305
  - lib/karafka/web/ui/views/jobs/_breadcrumbs.erb
@@ -313,6 +324,7 @@ files:
313
324
  - lib/karafka/web/ui/views/status/_breadcrumbs.erb
314
325
  - lib/karafka/web/ui/views/status/_failure.erb
315
326
  - lib/karafka/web/ui/views/status/_halted.erb
327
+ - lib/karafka/web/ui/views/status/_info.erb
316
328
  - lib/karafka/web/ui/views/status/_success.erb
317
329
  - lib/karafka/web/ui/views/status/_warning.erb
318
330
  - lib/karafka/web/ui/views/status/failures/_connection.erb
@@ -321,6 +333,7 @@ files:
321
333
  - lib/karafka/web/ui/views/status/failures/_partitions.erb
322
334
  - lib/karafka/web/ui/views/status/failures/_state_calculation.erb
323
335
  - lib/karafka/web/ui/views/status/failures/_topics.erb
336
+ - lib/karafka/web/ui/views/status/info/_components.erb
324
337
  - lib/karafka/web/ui/views/status/show.erb
325
338
  - lib/karafka/web/ui/views/status/warnings/_pro_subscription.erb
326
339
  - lib/karafka/web/version.rb
metadata.gz.sig CHANGED
Binary file