karafka-web 0.8.0.rc1 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +9 -1
  4. data/Gemfile.lock +8 -8
  5. data/karafka-web.gemspec +2 -2
  6. data/lib/karafka/web/config.rb +4 -0
  7. data/lib/karafka/web/contracts/config.rb +1 -0
  8. data/lib/karafka/web/installer.rb +3 -2
  9. data/lib/karafka/web/management/actions/enable.rb +5 -0
  10. data/lib/karafka/web/management/actions/extend_boot_file.rb +3 -1
  11. data/lib/karafka/web/management/migrations/1706607960_introduce_lag_total_in_metrics.rb +38 -0
  12. data/lib/karafka/web/management/migrations/1706607960_introduce_lag_total_in_states.rb +22 -0
  13. data/lib/karafka/web/management/migrations/1706611396_rename_lag_total_to_lag_hybrid_in_metrics.rb +36 -0
  14. data/lib/karafka/web/management/migrations/1706611396_rename_lag_total_to_lag_hybrid_in_states.rb +21 -0
  15. data/lib/karafka/web/processing/consumers/aggregators/metrics.rb +7 -9
  16. data/lib/karafka/web/processing/consumers/aggregators/state.rb +8 -9
  17. data/lib/karafka/web/processing/consumers/contracts/aggregated_stats.rb +1 -2
  18. data/lib/karafka/web/processing/consumers/contracts/topic_stats.rb +1 -2
  19. data/lib/karafka/web/ui/controllers/consumers.rb +1 -1
  20. data/lib/karafka/web/ui/models/metrics/charts/topics.rb +5 -5
  21. data/lib/karafka/web/ui/models/partition.rb +22 -0
  22. data/lib/karafka/web/ui/models/process.rb +3 -14
  23. data/lib/karafka/web/ui/pro/app.rb +4 -0
  24. data/lib/karafka/web/ui/pro/controllers/consumers.rb +10 -4
  25. data/lib/karafka/web/ui/pro/controllers/health.rb +12 -0
  26. data/lib/karafka/web/ui/pro/views/consumers/_consumer.erb +1 -1
  27. data/lib/karafka/web/ui/pro/views/consumers/_counters.erb +2 -2
  28. data/lib/karafka/web/ui/pro/views/consumers/consumer/_partition.erb +3 -3
  29. data/lib/karafka/web/ui/pro/views/consumers/consumer/_subscription_group.erb +2 -2
  30. data/lib/karafka/web/ui/pro/views/consumers/index.erb +1 -1
  31. data/lib/karafka/web/ui/pro/views/dashboard/index.erb +3 -3
  32. data/lib/karafka/web/ui/pro/views/health/_breadcrumbs.erb +8 -0
  33. data/lib/karafka/web/ui/pro/views/health/_consumer_group_header.erb +14 -0
  34. data/lib/karafka/web/ui/pro/views/health/_partition.erb +1 -9
  35. data/lib/karafka/web/ui/pro/views/health/_partition_lags.erb +24 -0
  36. data/lib/karafka/web/ui/pro/views/health/_partition_offset.erb +1 -1
  37. data/lib/karafka/web/ui/pro/views/health/_tabs.erb +9 -0
  38. data/lib/karafka/web/ui/pro/views/health/changes.erb +1 -15
  39. data/lib/karafka/web/ui/pro/views/health/lags.erb +52 -0
  40. data/lib/karafka/web/ui/pro/views/health/offsets.erb +2 -15
  41. data/lib/karafka/web/ui/pro/views/health/overview.erb +2 -17
  42. data/lib/karafka/web/ui/views/consumers/_counters.erb +2 -2
  43. data/lib/karafka/web/ui/views/consumers/index.erb +1 -1
  44. data/lib/karafka/web/ui/views/dashboard/index.erb +3 -3
  45. data/lib/karafka/web/version.rb +1 -1
  46. data.tar.gz.sig +0 -0
  47. metadata +13 -6
  48. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d3e890ec145255109e1f29ea742e6e0ed2887b0b4ca1de6047973816307744c1
4
- data.tar.gz: 55766edeb8f40c68c1200b577697728037882e2af3dd0dfb9723741be0343b7e
3
+ metadata.gz: 69148848d89de8a7c5a1cebec7d3f5c0c60e3bd85caa41eedea8b5082dd6bf85
4
+ data.tar.gz: c0f6acfdd3e732ebfc29a6e954cd8a1f8d2c75dff77eba28978a99d6394d4087
5
5
  SHA512:
6
- metadata.gz: 29dccc30c89888815b33a703317ca44bcf1b0a5f37228e9db7ba26eeba2d3b5ce5a66f5246121c4b9c1bb9d248e9027f3e8992f324109fd807d62a94c70c47ff
7
- data.tar.gz: 32ed3012d866810f85db46c8c82f20888693e1c533edb9bcdc54cf098c2bece4478e8fe82f8a3c62c313f219d622f8b42e25bd4f38793d82951dec311f164c5c
6
+ metadata.gz: a89e22500fc5fedf8e432b666a84cf092d13c9b2747a4d08dc983d8ffba2f077781f06a131631b6315c64764bc00b7d06265a4d9fcd8c11bdd74d47eda5041f9
7
+ data.tar.gz: 7d29af2874b1df83af78fee69af89021bdcd1fa705686370f59ab276ee6808eef98e4a0b98dd273158888b472cea203bcca84f494e84c178f7733404b080166a
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,6 +1,14 @@
1
1
  # Karafka Web changelog
2
2
 
3
- ## 0.8.0 (Unreleased)
3
+ ## 0.8.1 (2024-02-01)
4
+ - [Enhancement] Introduce "Lags" health view.
5
+ - [Enhancement] Remove "Stored Lag" and "Committed Offset" from Health Overview due to Lags Tab.
6
+ - [Enhancement] Report lag on consumers that did not yet marked offsets.
7
+ - [Enhancement] Use more accurate lag reporting that compensates for lack of stored lag.
8
+ - [Fix] When first message after process start is crashed without DLQ lag is not reported.
9
+ - [Fix] Wrong order of enabled injection causes fresh install to crash.
10
+
11
+ ## 0.8.0 (2024-01-26)
4
12
  - **[Feature]** Provide ability to sort table data for part of the views (note: not all attributes can be sorted due to technical limitations of sub-components fetching from Kafka).
5
13
  - **[Feature]** Track and report pause timeouts via "Changes" view in Health.
6
14
  - **[Feature]** Introduce pending jobs visibility alongside of running jobs both in total and per process.
data/Gemfile.lock CHANGED
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka-web (0.8.0.rc1)
4
+ karafka-web (0.8.1)
5
5
  erubi (~> 1.4)
6
- karafka (>= 2.3.0.rc1, < 2.4.0)
7
- karafka-core (>= 2.3.0.rc1, < 2.4.0)
6
+ karafka (>= 2.3.0, < 2.4.0)
7
+ karafka-core (>= 2.3.0, < 2.4.0)
8
8
  roda (~> 3.68, >= 3.69)
9
9
  tilt (~> 2.0)
10
10
 
@@ -36,13 +36,13 @@ GEM
36
36
  ffi (1.16.3)
37
37
  i18n (1.14.1)
38
38
  concurrent-ruby (~> 1.0)
39
- karafka (2.3.0.rc1)
40
- karafka-core (>= 2.3.0.rc1, < 2.4.0)
39
+ karafka (2.3.0)
40
+ karafka-core (>= 2.3.0, < 2.4.0)
41
41
  waterdrop (>= 2.6.12, < 3.0.0)
42
42
  zeitwerk (~> 2.3)
43
- karafka-core (2.3.0.rc1)
44
- karafka-rdkafka (>= 0.14.7, < 0.15.0)
45
- karafka-rdkafka (0.14.7)
43
+ karafka-core (2.3.0)
44
+ karafka-rdkafka (>= 0.14.8, < 0.15.0)
45
+ karafka-rdkafka (0.14.8)
46
46
  ffi (~> 1.15)
47
47
  mini_portile2 (~> 2.6)
48
48
  rake (> 12)
data/karafka-web.gemspec CHANGED
@@ -17,8 +17,8 @@ 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.3.0.rc1', '< 2.4.0'
21
- spec.add_dependency 'karafka-core', '>= 2.3.0.rc1', '< 2.4.0'
20
+ spec.add_dependency 'karafka', '>= 2.3.0', '< 2.4.0'
21
+ spec.add_dependency 'karafka-core', '>= 2.3.0', '< 2.4.0'
22
22
  spec.add_dependency 'roda', '~> 3.68', '>= 3.69'
23
23
  spec.add_dependency 'tilt', '~> 2.0'
24
24
 
@@ -6,6 +6,10 @@ module Karafka
6
6
  class Config
7
7
  include ::Karafka::Core::Configurable
8
8
 
9
+ # Is the Web UI enabled and things were configured.
10
+ # Automatically set to true in case things got enabled.
11
+ setting :enabled, default: false
12
+
9
13
  # How long do we consider the process alive without receiving status info about it
10
14
  # For this long we also display dead processes (shutdown) in the UI
11
15
  # This is used both in the processing for eviction and in the UI
@@ -10,6 +10,7 @@ module Karafka
10
10
  # Use the same regexp as Karafka for topics validation
11
11
  TOPIC_REGEXP = ::Karafka::Contracts::TOPIC_REGEXP
12
12
 
13
+ required(:enabled) { |val| [true, false, nil].include?(val) }
13
14
  required(:ttl) { |val| val.is_a?(Numeric) && val.positive? }
14
15
 
15
16
  nested(:topics) do
@@ -15,17 +15,18 @@ module Karafka
15
15
  puts
16
16
  puts 'Installing Karafka Web UI...'
17
17
  puts
18
+ Management::Actions::ExtendBootFile.new.call
19
+ puts
18
20
  puts 'Creating necessary topics and populating state data...'
19
21
  puts
20
22
  Management::Actions::CreateTopics.new.call(replication_factor)
21
23
  wait_for_topics
24
+ enable!
22
25
  Management::Actions::CreateInitialStates.new.call
23
26
  puts
24
27
  puts 'Running data migrations...'
25
28
  Management::Actions::MigrateStatesData.new.call
26
29
  puts
27
- Management::Actions::ExtendBootFile.new.call
28
- puts
29
30
  puts("Installation #{green('completed')}. Have fun!")
30
31
  puts
31
32
  end
@@ -10,6 +10,11 @@ module Karafka
10
10
  class Enable < Base
11
11
  # Enables routing consumer group and subscribes Web-UI listeners
12
12
  def call
13
+ # Prevent double enabling
14
+ return if ::Karafka::Web.config.enabled
15
+
16
+ ::Karafka::Web.config.enabled = true
17
+
13
18
  extend_routing
14
19
  setup_tracking_activity
15
20
 
@@ -22,7 +22,9 @@ module Karafka
22
22
 
23
23
  # Adds needed code
24
24
  def call
25
- if File.read(Karafka.boot_file).include?(ENABLER_CODE)
25
+ # We detect this that way so in case our template or user has enabled as a comment
26
+ # it still adds the template and runs install
27
+ if File.readlines(Karafka.boot_file).any? { |line| line.start_with?(ENABLER_CODE) }
26
28
  puts "Web UI #{already} installed."
27
29
  else
28
30
  puts 'Updating the Karafka boot file...'
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ module Management
6
+ module Migrations
7
+ # Moves to using lag total as a normalization for both lags
8
+ class IntroduceLagTotalInMetrics < Base
9
+ self.versions_until = '1.2.0'
10
+ self.type = :consumers_metrics
11
+
12
+ # @param state [Hash]
13
+ def migrate(state)
14
+ state[:aggregated].each_value do |metrics|
15
+ metrics.each do |metric|
16
+ metric.last[:lag_total] = metric.last[:lag_stored]
17
+ metric.last.delete(:lag_stored)
18
+ metric.last.delete(:lag)
19
+ end
20
+ end
21
+
22
+ state[:consumer_groups].each_value do |metrics|
23
+ metrics.each do |metric_group|
24
+ metric_group.last.each_value do |metric|
25
+ metric.each_value do |sample|
26
+ sample[:lag_total] = sample[:lag_stored]
27
+ sample.delete(:lag_stored)
28
+ sample.delete(:lag)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ module Management
6
+ module Migrations
7
+ # Moves to using lag total as a normalization for both lags
8
+ class IntroduceLagTotalInStates < Base
9
+ self.versions_until = '1.3.0'
10
+ self.type = :consumers_state
11
+
12
+ # @param state [Hash]
13
+ def migrate(state)
14
+ state[:stats][:lag_total] = state[:stats][:lag_stored]
15
+ state[:stats].delete(:lag)
16
+ state[:stats].delete(:lag_stored)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ module Management
6
+ module Migrations
7
+ # Renames total lag to hybrid to better represent what it is
8
+ class RenameLagTotalToLagHybridInMetrics < Base
9
+ self.versions_until = '1.2.1'
10
+ self.type = :consumers_metrics
11
+
12
+ # @param state [Hash]
13
+ def migrate(state)
14
+ state[:aggregated].each_value do |metrics|
15
+ metrics.each do |metric|
16
+ metric.last[:lag_hybrid] = metric.last[:lag_total] || 0
17
+ metric.last.delete(:lag_total)
18
+ end
19
+ end
20
+
21
+ state[:consumer_groups].each_value do |metrics|
22
+ metrics.each do |metric_group|
23
+ metric_group.last.each_value do |metric|
24
+ metric.each_value do |sample|
25
+ sample[:lag_hybrid] = sample[:lag_total]
26
+ sample.delete(:lag_total)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ module Management
6
+ module Migrations
7
+ # Renames total lag to hybrid to better represent what it is
8
+ class RenameLagTotalToLagHybridInStates < Base
9
+ self.versions_until = '1.3.1'
10
+ self.type = :consumers_state
11
+
12
+ # @param state [Hash]
13
+ def migrate(state)
14
+ state[:stats][:lag_hybrid] = state[:stats][:lag_total] || 0
15
+ state[:stats].delete(:lag_total)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -11,7 +11,7 @@ module Karafka
11
11
  class Metrics < Base
12
12
  # Current schema version
13
13
  # This is used for detecting incompatible changes and writing migrations
14
- SCHEMA_VERSION = '1.1.2'
14
+ SCHEMA_VERSION = '1.2.1'
15
15
 
16
16
  def initialize
17
17
  super
@@ -92,12 +92,11 @@ module Karafka
92
92
  cgs = {}
93
93
 
94
94
  iterate_partitions_data do |group_name, topic_name, partitions_data|
95
- lags = partitions_data
96
- .map { |p_details| p_details.fetch(:lag, -1) }
97
- .reject(&:negative?)
98
-
99
- lags_stored = partitions_data
100
- .map { |p_details| p_details.fetch(:lag_stored, -1) }
95
+ lags_hybrid = partitions_data
96
+ .map do |p_details|
97
+ lag_stored = p_details.fetch(:lag_stored, -1)
98
+ lag_stored.negative? ? p_details.fetch(:lag, -1) : lag_stored
99
+ end
101
100
  .reject(&:negative?)
102
101
 
103
102
  offsets_hi = partitions_data
@@ -121,8 +120,7 @@ module Karafka
121
120
 
122
121
  cgs[group_name] ||= {}
123
122
  cgs[group_name][topic_name] = {
124
- lag_stored: lags_stored.sum,
125
- lag: lags.sum,
123
+ lag_hybrid: lags_hybrid.sum,
126
124
  pace: offsets_hi.sum,
127
125
  # Take max last stable offset duration without any change. This can
128
126
  # indicate a hanging transaction, because the offset will not move forward
@@ -20,7 +20,7 @@ module Karafka
20
20
  # Current schema version
21
21
  # This can be used in the future for detecting incompatible changes and writing
22
22
  # migrations
23
- SCHEMA_VERSION = '1.2.2'
23
+ SCHEMA_VERSION = '1.3.1'
24
24
 
25
25
  # @param schema_manager [Karafka::Web::Processing::Consumers::SchemaManager] schema
26
26
  # manager that tracks the compatibility of schemas.
@@ -127,8 +127,7 @@ module Karafka
127
127
  stats[:processes] = 0
128
128
  stats[:rss] = 0
129
129
  stats[:listeners] = { active: 0, standby: 0 }
130
- stats[:lag] = 0
131
- stats[:lag_stored] = 0
130
+ stats[:lag_hybrid] = 0
132
131
  stats[:bytes_received] = 0
133
132
  stats[:bytes_sent] = 0
134
133
  utilization = 0
@@ -140,12 +139,13 @@ module Karafka
140
139
  report_stats = report[:stats]
141
140
  report_process = report[:process]
142
141
 
143
- lags = []
144
- lags_stored = []
142
+ lags_hybrid = []
145
143
 
146
144
  iterate_partitions(report) do |partition_stats|
147
- lags << partition_stats[:lag]
148
- lags_stored << partition_stats[:lag_stored]
145
+ lag_stored = partition_stats[:lag_stored]
146
+ lag = partition_stats[:lag]
147
+
148
+ lags_hybrid << (lag_stored.negative? ? lag : lag_stored)
149
149
  end
150
150
 
151
151
  stats[:busy] += report_stats[:busy]
@@ -157,8 +157,7 @@ module Karafka
157
157
  stats[:listeners][:standby] += report_process[:listeners][:standby]
158
158
  stats[:processes] += 1
159
159
  stats[:rss] += report_process[:memory_usage]
160
- stats[:lag] += lags.compact.reject(&:negative?).sum
161
- stats[:lag_stored] += lags_stored.compact.reject(&:negative?).sum
160
+ stats[:lag_hybrid] += lags_hybrid.compact.reject(&:negative?).sum
162
161
  utilization += report_stats[:utilization]
163
162
  end
164
163
 
@@ -21,8 +21,7 @@ module Karafka
21
21
  required(:processes) { |val| val.is_a?(Integer) && val >= 0 }
22
22
  required(:rss) { |val| val.is_a?(Numeric) && val >= 0 }
23
23
  required(:utilization) { |val| val.is_a?(Numeric) && val >= 0 }
24
- required(:lag_stored) { |val| val.is_a?(Integer) }
25
- required(:lag) { |val| val.is_a?(Integer) }
24
+ required(:lag_hybrid) { |val| val.is_a?(Integer) }
26
25
 
27
26
  nested(:listeners) do
28
27
  required(:active) { |val| val.is_a?(Integer) && val >= 0 }
@@ -9,8 +9,7 @@ module Karafka
9
9
  class TopicStats < Web::Contracts::Base
10
10
  configure
11
11
 
12
- required(:lag_stored) { |val| val.is_a?(Integer) }
13
- required(:lag) { |val| val.is_a?(Integer) }
12
+ required(:lag_hybrid) { |val| val.is_a?(Integer) }
14
13
  required(:pace) { |val| val.is_a?(Integer) }
15
14
  required(:ls_offset_fd) { |val| val.is_a?(Integer) && val >= 0 }
16
15
  end
@@ -9,7 +9,7 @@ module Karafka
9
9
  self.sortable_attributes = %w[
10
10
  name
11
11
  started_at
12
- lag_stored
12
+ lag_hybrid
13
13
  ].freeze
14
14
 
15
15
  # List page with consumers
@@ -16,17 +16,17 @@ module Karafka
16
16
 
17
17
  # @return [String] JSON with lags of each of the topics + total lag of all the topics
18
18
  # from all the consumer groups.
19
- def lags_stored
19
+ def lags_hybrid
20
20
  total = Hash.new { |h, v| h[v] = 0 }
21
21
 
22
22
  @data.to_h.each_value do |metrics|
23
23
  metrics.each do |metric|
24
24
  time = metric.first
25
- lag_stored = metric.last[:lag_stored]
25
+ lag_hybrid = metric.last[:lag_hybrid]
26
26
 
27
- if lag_stored
27
+ if lag_hybrid
28
28
  total[time] ||= 0
29
- total[time] += lag_stored
29
+ total[time] += lag_hybrid
30
30
  else
31
31
  next if total.key?(time)
32
32
 
@@ -37,7 +37,7 @@ module Karafka
37
37
 
38
38
  # Extract the lag stored only from all the data
39
39
  per_topic = @data.to_h.map do |topic, metrics|
40
- extracted = metrics.map { |metric| [metric.first, metric.last[:lag_stored]] }
40
+ extracted = metrics.map { |metric| [metric.first, metric.last[:lag_hybrid]] }
41
41
 
42
42
  [topic, extracted]
43
43
  end.to_h
@@ -6,6 +6,28 @@ module Karafka
6
6
  module Models
7
7
  # Single topic partition data representation model
8
8
  class Partition < Lib::HashProxy
9
+ # @param args [Object] anything hash proxy accepts
10
+ def initialize(*args)
11
+ super
12
+
13
+ # We initialize those here because we want to have them stored in the internal has
14
+ # for sorting
15
+ lag_hybrid
16
+ lag_hybrid_d
17
+ end
18
+
19
+ # Because `lag_stored` is not available until first marking, we fallback to the lag
20
+ # value that may be behind but is always available until stored lag is available.
21
+ # @return [Integer] hybrid log value
22
+ def lag_hybrid
23
+ self[:lag_hybrid] ||= lag_stored.negative? ? lag : lag_stored
24
+ end
25
+
26
+ # @return [Integer] hybrid log delta
27
+ def lag_hybrid_d
28
+ self[:lag_hybrid_d] ||= lag_stored.negative? ? lag_d : lag_stored_d
29
+ end
30
+
9
31
  # @return [Symbol] one of three states in which LSO can be in the correlation to given
10
32
  # partition in the context of a consumer group.
11
33
  #
@@ -41,24 +41,13 @@ module Karafka
41
41
  .then { |jobs| Jobs.new(jobs) }
42
42
  end
43
43
 
44
- # @return [Integer] collective stored lag on this process
45
- def lag_stored
44
+ # @return [Integer] collective hybrid lag on this process
45
+ def lag_hybrid
46
46
  consumer_groups
47
47
  .flat_map(&:subscription_groups)
48
48
  .flat_map(&:topics)
49
49
  .flat_map(&:partitions)
50
- .map(&:lag_stored)
51
- .delete_if(&:negative?)
52
- .sum
53
- end
54
-
55
- # @return [Integer] collective lag on this process
56
- def lag
57
- consumer_groups
58
- .flat_map(&:subscription_groups)
59
- .flat_map(&:topics)
60
- .flat_map(&:partitions)
61
- .map(&:lag)
50
+ .map(&:lag_hybrid)
62
51
  .delete_if(&:negative?)
63
52
  .sum
64
53
  end
@@ -163,6 +163,10 @@ module Karafka
163
163
  r.on 'health' do
164
164
  controller = Controllers::Health.new(params)
165
165
 
166
+ r.get 'lags' do
167
+ controller.lags
168
+ end
169
+
166
170
  r.get 'offsets' do
167
171
  controller.offsets
168
172
  end
@@ -19,11 +19,11 @@ module Karafka
19
19
  # Controller for displaying consumers states and details about them
20
20
  class Consumers < Ui::Controllers::Base
21
21
  self.sortable_attributes = %w[
22
+ id
22
23
  name
23
24
  started_at
24
- lag_stored
25
- id
26
- lag_stored_d
25
+ lag_hybrid
26
+ lag_hybrid_d
27
27
  committed_offset
28
28
  stored_offset
29
29
  fetch_state
@@ -93,7 +93,13 @@ module Karafka
93
93
 
94
94
  # We want to have sorting but on a per subscription group basis and not to sort
95
95
  # everything
96
- @process.consumer_groups.each { |subscription_group| refine(subscription_group) }
96
+ @process.consumer_groups.each do |consumer_group|
97
+ # We need to initialize the whole structure so dynamic fields are also built into
98
+ # the underlying hashes for sorting
99
+ consumer_group.subscription_groups.flat_map(&:topics).flat_map(&:partitions)
100
+
101
+ refine(consumer_group)
102
+ end
97
103
 
98
104
  render
99
105
  end
@@ -20,8 +20,12 @@ module Karafka
20
20
  class Health < Ui::Controllers::Base
21
21
  self.sortable_attributes = %w[
22
22
  id
23
+ lag
24
+ lag_d
23
25
  lag_stored
24
26
  lag_stored_d
27
+ lag_hybrid
28
+ lag_hybrid_d
25
29
  committed_offset
26
30
  committed_offset_fd
27
31
  stored_offset
@@ -50,6 +54,14 @@ module Karafka
50
54
  render
51
55
  end
52
56
 
57
+ # Displays details about lags and their progression/statuses
58
+ def lags
59
+ # Same data as overview but presented differently
60
+ overview
61
+
62
+ render
63
+ end
64
+
53
65
  # Displays details about offsets and their progression/statuses
54
66
  def offsets
55
67
  # Same data as overview but presented differently
@@ -71,6 +71,6 @@
71
71
  </td>
72
72
 
73
73
  <td>
74
- <%= process.lag_stored %>
74
+ <%= process.lag_hybrid %>
75
75
  </td>
76
76
  </tr>
@@ -19,9 +19,9 @@
19
19
  </li>
20
20
  <li class="col-sm">
21
21
  <div class="count mb-1">
22
- <%= number_with_delimiter @counters.lag_stored, ' ' %>
22
+ <%= number_with_delimiter @counters.lag_hybrid, ' ' %>
23
23
  </div>
24
- <div class="desc">Lag stored</div>
24
+ <div class="desc">Total lag</div>
25
25
  </li>
26
26
  <li class="col-sm">
27
27
  <a href="<%= root_path('jobs/running') %>">
@@ -3,11 +3,11 @@
3
3
  <%= partition.id %>
4
4
  </td>
5
5
  <td>
6
- <%== lag_with_label partition.lag_stored %>
6
+ <%== lag_with_label partition.lag_hybrid %>
7
7
  </td>
8
8
  <td>
9
- <span class="badge <%= lag_trend_bg(partition.lag_stored_d) %>">
10
- <%= partition.lag_stored_d %>
9
+ <span class="badge <%= lag_trend_bg(partition.lag_hybrid_d) %>">
10
+ <%= partition.lag_hybrid_d %>
11
11
  </span>
12
12
  </td>
13
13
  <td>
@@ -104,8 +104,8 @@
104
104
  <tr class="align-middle">
105
105
  </tr>
106
106
  <th><%== sort_link('Partition', :id) %></th>
107
- <th><%== sort_link(:lag_stored) %></th>
108
- <th><%== sort_link('Lag stored trend', :lag_stored_d) %></th>
107
+ <th><%== sort_link('Lag', :lag_hybrid) %></th>
108
+ <th><%== sort_link('Lag trend', :lag_hybrid_d) %></th>
109
109
  <th><%== sort_link(:committed_offset) %></th>
110
110
  <th><%== sort_link(:stored_offset) %></th>
111
111
  <th><%== sort_link(:fetch_state) %></th>
@@ -17,7 +17,7 @@
17
17
  <th class="col-sm-1">Memory</th>
18
18
  <th class="col-sm-1">Performance</th>
19
19
  <th class="col-sm-1">Load</th>
20
- <th class="col-sm-1"><%== sort_link(:lag_stored) %></th>
20
+ <th class="col-sm-1"><%== sort_link('Lag', :lag_hybrid) %></th>
21
21
  </tr>
22
22
  </thead>
23
23
  <tbody>
@@ -13,7 +13,7 @@
13
13
  <ul class="nav nav-tabs" id="graphs1" role="tablist">
14
14
  <%== partial 'shared/tab_nav', locals: { title: 'Messages', id: 'messages', active: true } %>
15
15
  <%== partial 'shared/tab_nav', locals: { title: 'Batches', id: 'batches' } %>
16
- <%== partial 'shared/tab_nav', locals: { title: 'Lags stored', id: 'lags-stored' } %>
16
+ <%== partial 'shared/tab_nav', locals: { title: 'Topics lags', id: 'topics-lags' } %>
17
17
  <%== partial 'shared/tab_nav', locals: { title: 'Topics pace', id: 'topics-pace' } %>
18
18
  <%== partial 'shared/tab_nav', locals: { title: 'Max LSO time', id: 'max-lso-time' } %>
19
19
  </ul>
@@ -29,8 +29,8 @@
29
29
  <%== partial 'shared/chart', locals: { data: data, id: 'batches' } %>
30
30
  </div>
31
31
 
32
- <div class="tab-pane" id="lags-stored" role="tabpanel">
33
- <%== partial 'shared/chart', locals: { data: @topics_charts.lags_stored, id: 'lags-stored' } %>
32
+ <div class="tab-pane" id="topics-lags" role="tabpanel">
33
+ <%== partial 'shared/chart', locals: { data: @topics_charts.lags_hybrid, id: 'topics-lags' } %>
34
34
  </div>
35
35
 
36
36
  <div class="tab-pane" id="topics-pace" role="tabpanel">
@@ -4,6 +4,14 @@
4
4
  </a>
5
5
  </li>
6
6
 
7
+ <% if current_path.include?('/lags') %>
8
+ <li class="breadcrumb-item">
9
+ <a href="<%= root_path('health', 'lags') %>">
10
+ Lags
11
+ </a>
12
+ </li>
13
+ <% end %>
14
+
7
15
  <% if current_path.include?('/overview') %>
8
16
  <li class="breadcrumb-item">
9
17
  <a href="<%= root_path('health', 'overview') %>">
@@ -0,0 +1,14 @@
1
+ <div class="row mb-4">
2
+ <div class="col-sm-8">
3
+ <h4 class="mb-4"><%= cg_name %></h4>
4
+ </div>
5
+
6
+ <div class="col-sm-4">
7
+ <span class="float-end">
8
+ Last rebalance:
9
+ <span class="badge bg-secondary">
10
+ <%== relative_time(details[:rebalanced_at]) %>
11
+ </span>
12
+ </span>
13
+ </div>
14
+ </div>
@@ -3,15 +3,7 @@
3
3
  <%= partition_id %>
4
4
  </td>
5
5
  <td>
6
- <%== lag_with_label details.lag_stored %>
7
- </td>
8
- <td>
9
- <span class="badge <%= lag_trend_bg(details.lag_stored_d) %>">
10
- <%= details.lag_stored_d %>
11
- </span>
12
- </td>
13
- <td>
14
- <%== offset_with_label topic_name, partition_id, details.committed_offset %>
6
+ <%== lag_with_label details.lag_hybrid %>
15
7
  </td>
16
8
  <td>
17
9
  <%== offset_with_label topic_name, partition_id, details.stored_offset %>
@@ -0,0 +1,24 @@
1
+ <tr class="align-middle <%= lso_risk_state_bg(details) %> status-row-<%= details.process.status %>">
2
+ <td>
3
+ <%= partition_id %>
4
+ </td>
5
+ <td>
6
+ <%== lag_with_label details.lag %>
7
+ </td>
8
+ <td>
9
+ <span class="badge <%= lag_trend_bg(details.lag_d) %>">
10
+ <%= details.lag_d %>
11
+ </span>
12
+ </td>
13
+ <td>
14
+ <%== lag_with_label details.lag_stored %>
15
+ </td>
16
+ <td>
17
+ <span class="badge <%= lag_trend_bg(details.lag_stored_d) %>">
18
+ <%= details.lag_stored_d %>
19
+ </span>
20
+ </td>
21
+ <td>
22
+ <%== poll_state_with_change_time_label(details.poll_state, details.poll_state_ch) %>
23
+ </td>
24
+ </tr>
@@ -3,7 +3,7 @@
3
3
  <%= partition_id %>
4
4
  </td>
5
5
  <td>
6
- <%== lag_with_label details.lag_stored %>
6
+ <%== lag_with_label details.lag_hybrid %>
7
7
  </td>
8
8
  <td>
9
9
  <%== offset_with_label topic_name, partition_id, details.committed_offset %>
@@ -12,6 +12,15 @@
12
12
  </a>
13
13
  </li>
14
14
 
15
+ <li class="nav-item">
16
+ <a
17
+ class="nav-link <%= nav_class(include: 'lags') %>"
18
+ href="<%= root_path('health', 'lags') %>"
19
+ >
20
+ Lags
21
+ </a>
22
+ </li>
23
+
15
24
  <li class="nav-item">
16
25
  <a
17
26
  class="nav-link <%= nav_class(include: 'offsets') %>"
@@ -6,23 +6,9 @@
6
6
  <%== partial 'health/tabs' %>
7
7
  <% end %>
8
8
 
9
-
10
9
  <% @stats.each_with_index do |(cg_name, details), index| %>
11
10
  <div class="container mb-5">
12
- <div class="row mb-4">
13
- <div class="col-sm-8">
14
- <h4 class="mb-4"><%= cg_name %></h4>
15
- </div>
16
-
17
- <div class="col-sm-4">
18
- <span class="float-end">
19
- Last rebalance:
20
- <span class="badge bg-secondary">
21
- <%== relative_time(details[:rebalanced_at]) %>
22
- </span>
23
- </span>
24
- </div>
25
- </div>
11
+ <%== partial 'health/consumer_group_header', locals: { cg_name: cg_name, details: details } %>
26
12
 
27
13
  <div class="row mb-3">
28
14
  <div class="col-sm-12">
@@ -0,0 +1,52 @@
1
+ <%== view_title('Consumers groups lags details') %>
2
+
3
+ <% if @stats.empty? %>
4
+ <%== partial 'health/no_data' %>
5
+ <% else %>
6
+ <%== partial 'health/tabs' %>
7
+ <% end %>
8
+
9
+ <% @stats.each_with_index do |(cg_name, details), index| %>
10
+ <div class="container mb-5">
11
+ <%== partial 'health/consumer_group_header', locals: { cg_name: cg_name, details: details } %>
12
+
13
+ <div class="row mb-3">
14
+ <div class="col-sm-12">
15
+ <% topics = details[:topics] %>
16
+ <% topics.each_with_index do |(topic_name, partitions), index| %>
17
+ <table class="processes bg-white table table-hover table-bordered table-striped align-middle <%= (index+1 < topics.size) ? 'mb-5' : 'mb-3' %>">
18
+ <thead>
19
+ <tr class="align-middle">
20
+ <th colspan="12">
21
+ <h5 class="mb-0"><%= topic_name %></h4>
22
+ </th>
23
+ </tr>
24
+ <tr class="align-middle">
25
+ <th><%== sort_link('Partition', :id) %></th>
26
+ <th><%== sort_link('Lag', :lag) %></th>
27
+ <th><%== sort_link('Lag trend', :lag_d) %></th>
28
+ <th><%== sort_link('Lag stored', :lag_stored) %></th>
29
+ <th><%== sort_link('Lag stored trend', :lag_stored_d) %></th>
30
+ <th><%== sort_link(:poll_state) %></th>
31
+ </tr>
32
+ </thead>
33
+ <tbody>
34
+ <% partitions.each do |partition_id, details| %>
35
+ <%==
36
+ partial(
37
+ 'health/partition_lags',
38
+ locals: {
39
+ topic_name: topic_name,
40
+ partition_id: partition_id,
41
+ details: details
42
+ }
43
+ )
44
+ %>
45
+ <% end %>
46
+ </tbody>
47
+ </table>
48
+ <% end %>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ <% end %>
@@ -8,20 +8,7 @@
8
8
 
9
9
  <% @stats.each_with_index do |(cg_name, details), index| %>
10
10
  <div class="container mb-5">
11
- <div class="row mb-4">
12
- <div class="col-sm-8">
13
- <h4 class="mb-4"><%= cg_name %></h4>
14
- </div>
15
-
16
- <div class="col-sm-4">
17
- <span class="float-end">
18
- Last rebalance:
19
- <span class="badge bg-secondary">
20
- <%== relative_time(details[:rebalanced_at]) %>
21
- </span>
22
- </span>
23
- </div>
24
- </div>
11
+ <%== partial 'health/consumer_group_header', locals: { cg_name: cg_name, details: details } %>
25
12
 
26
13
  <div class="row mb-3">
27
14
  <div class="col-sm-12">
@@ -36,7 +23,7 @@
36
23
  </tr>
37
24
  <tr class="align-middle">
38
25
  <th><%== sort_link('Partition', :id) %></th>
39
- <th><%== sort_link(:lag_stored) %></th>
26
+ <th><%== sort_link('Lag', :lag_hybrid) %></th>
40
27
  <th><%== sort_link(:committed_offset) %></th>
41
28
  <th><%== sort_link('Committed offset change', :committed_offset_fd) %></th>
42
29
  <th><%== sort_link(:stored_offset) %></th>
@@ -8,20 +8,7 @@
8
8
 
9
9
  <% @stats.each_with_index do |(cg_name, details), index| %>
10
10
  <div class="container mb-5">
11
- <div class="row mb-4">
12
- <div class="col-sm-8">
13
- <h4 class="mb-4"><%= cg_name %></h4>
14
- </div>
15
-
16
- <div class="col-sm-4">
17
- <span class="float-end">
18
- Last rebalance:
19
- <span class="badge bg-secondary">
20
- <%== relative_time(details[:rebalanced_at]) %>
21
- </span>
22
- </span>
23
- </div>
24
- </div>
11
+ <%== partial 'health/consumer_group_header', locals: { cg_name: cg_name, details: details } %>
25
12
 
26
13
  <div class="row mb-3">
27
14
  <div class="col-sm-12">
@@ -36,9 +23,7 @@
36
23
  </tr>
37
24
  <tr class="align-middle">
38
25
  <th><%== sort_link('Partition', :id) %></th>
39
- <th><%== sort_link(:lag_stored) %></th>
40
- <th><%== sort_link('Lag stored trend', :lag_stored_d) %></th>
41
- <th><%== sort_link(:committed_offset) %></th>
26
+ <th><%== sort_link('Lag', :lag_hybrid) %></th>
42
27
  <th><%== sort_link(:stored_offset) %></th>
43
28
  <th><%== sort_link(:fetch_state) %></th>
44
29
  <th><%== sort_link(:poll_state) %></th>
@@ -19,9 +19,9 @@
19
19
  </li>
20
20
  <li class="col-sm">
21
21
  <div class="count mb-1">
22
- <%= number_with_delimiter @counters.lag_stored, ' ' %>
22
+ <%= number_with_delimiter @counters.lag_hybrid, ' ' %>
23
23
  </div>
24
- <div class="desc">Lag stored</div>
24
+ <div class="desc">Total lag</div>
25
25
  </li>
26
26
  <li class="col-sm">
27
27
  <a href="<%= root_path('jobs/running') %>">
@@ -16,7 +16,7 @@
16
16
  <th class="col-sm-2"><%== sort_link('Started', :started_at, rev: true) %></th>
17
17
  <th class="col-sm-1">Memory</th>
18
18
  <th class="col-sm-1">Utilization</th>
19
- <th class="col-sm-1"><%== sort_link(:lag_stored) %></th>
19
+ <th class="col-sm-1"><%== sort_link('Lag', :lag_hybrid) %></th>
20
20
  </tr>
21
21
  </thead>
22
22
  <tbody>
@@ -23,7 +23,7 @@
23
23
  <ul class="nav nav-tabs" id="graphs1" role="tablist">
24
24
  <%== partial 'shared/tab_nav', locals: { title: 'Messages', id: 'messages', active: true } %>
25
25
  <%== partial 'shared/tab_nav', locals: { title: 'Batches', id: 'batches' } %>
26
- <%== partial 'shared/tab_nav', locals: { title: 'Lags stored', id: 'lags-stored' } %>
26
+ <%== partial 'shared/tab_nav', locals: { title: 'Topics Lags', id: 'topics-lags' } %>
27
27
  <%== partial 'shared/tab_nav', locals: { title: 'Topics pace', id: 'topics-pace' } %>
28
28
  <%== partial 'shared/tab_nav', locals: { title: 'Max LSO time', id: 'max-lso-time' } %>
29
29
  </ul>
@@ -39,10 +39,10 @@
39
39
  <%== partial 'shared/chart', locals: { data: data, id: 'batches' } %>
40
40
  </div>
41
41
 
42
- <div class="tab-pane" id="lags-stored" role="tabpanel">
42
+ <div class="tab-pane" id="topics-lags" role="tabpanel">
43
43
  <%== partial 'dashboard/feature_pro' %>
44
44
  <% data = { enqueued: set.call, busy: set.call }.to_json %>
45
- <%== partial 'shared/chart', locals: { data: data, id: 'lags-stored', blurred: true } %>
45
+ <%== partial 'shared/chart', locals: { data: data, id: 'topics-lags', blurred: true } %>
46
46
  </div>
47
47
 
48
48
  <div class="tab-pane" id="topics-pace" role="tabpanel">
@@ -3,6 +3,6 @@
3
3
  module Karafka
4
4
  module Web
5
5
  # Current gem version
6
- VERSION = '0.8.0.rc1'
6
+ VERSION = '0.8.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.8.0.rc1
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Mensfeld
@@ -35,7 +35,7 @@ cert_chain:
35
35
  AnG1dJU+yL2BK7vaVytLTstJME5mepSZ46qqIJXMuWob/YPDmVaBF39TDSG9e34s
36
36
  msG3BiCqgOgHAnL23+CN3Rt8MsuRfEtoTKpJVcCfoEoNHOkc
37
37
  -----END CERTIFICATE-----
38
- date: 2024-01-21 00:00:00.000000000 Z
38
+ date: 2024-02-01 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.3.0.rc1
60
+ version: 2.3.0
61
61
  - - "<"
62
62
  - !ruby/object:Gem::Version
63
63
  version: 2.4.0
@@ -67,7 +67,7 @@ dependencies:
67
67
  requirements:
68
68
  - - ">="
69
69
  - !ruby/object:Gem::Version
70
- version: 2.3.0.rc1
70
+ version: 2.3.0
71
71
  - - "<"
72
72
  - !ruby/object:Gem::Version
73
73
  version: 2.4.0
@@ -77,7 +77,7 @@ dependencies:
77
77
  requirements:
78
78
  - - ">="
79
79
  - !ruby/object:Gem::Version
80
- version: 2.3.0.rc1
80
+ version: 2.3.0
81
81
  - - "<"
82
82
  - !ruby/object:Gem::Version
83
83
  version: 2.4.0
@@ -87,7 +87,7 @@ dependencies:
87
87
  requirements:
88
88
  - - ">="
89
89
  - !ruby/object:Gem::Version
90
- version: 2.3.0.rc1
90
+ version: 2.3.0
91
91
  - - "<"
92
92
  - !ruby/object:Gem::Version
93
93
  version: 2.4.0
@@ -205,6 +205,10 @@ files:
205
205
  - lib/karafka/web/management/migrations/1700234522_remove_processing_from_consumers_state.rb
206
206
  - lib/karafka/web/management/migrations/1704722380_split_listeners_into_active_and_paused_in_metrics.rb
207
207
  - lib/karafka/web/management/migrations/1704722380_split_listeners_into_active_and_paused_in_states.rb
208
+ - lib/karafka/web/management/migrations/1706607960_introduce_lag_total_in_metrics.rb
209
+ - lib/karafka/web/management/migrations/1706607960_introduce_lag_total_in_states.rb
210
+ - lib/karafka/web/management/migrations/1706611396_rename_lag_total_to_lag_hybrid_in_metrics.rb
211
+ - lib/karafka/web/management/migrations/1706611396_rename_lag_total_to_lag_hybrid_in_states.rb
208
212
  - lib/karafka/web/management/migrator.rb
209
213
  - lib/karafka/web/processing/consumer.rb
210
214
  - lib/karafka/web/processing/consumers/aggregators/base.rb
@@ -369,12 +373,15 @@ files:
369
373
  - lib/karafka/web/ui/pro/views/explorer/topic/_empty.erb
370
374
  - lib/karafka/web/ui/pro/views/explorer/topic/_limited.erb
371
375
  - lib/karafka/web/ui/pro/views/health/_breadcrumbs.erb
376
+ - lib/karafka/web/ui/pro/views/health/_consumer_group_header.erb
372
377
  - lib/karafka/web/ui/pro/views/health/_no_data.erb
373
378
  - lib/karafka/web/ui/pro/views/health/_partition.erb
379
+ - lib/karafka/web/ui/pro/views/health/_partition_lags.erb
374
380
  - lib/karafka/web/ui/pro/views/health/_partition_offset.erb
375
381
  - lib/karafka/web/ui/pro/views/health/_partition_times.erb
376
382
  - lib/karafka/web/ui/pro/views/health/_tabs.erb
377
383
  - lib/karafka/web/ui/pro/views/health/changes.erb
384
+ - lib/karafka/web/ui/pro/views/health/lags.erb
378
385
  - lib/karafka/web/ui/pro/views/health/offsets.erb
379
386
  - lib/karafka/web/ui/pro/views/health/overview.erb
380
387
  - lib/karafka/web/ui/pro/views/jobs/_job.erb
metadata.gz.sig CHANGED
Binary file