karafka-web 0.7.6 → 0.7.8

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 (42) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/ci.yml +2 -2
  4. data/CHANGELOG.md +11 -0
  5. data/Gemfile.lock +27 -17
  6. data/README.md +3 -0
  7. data/bin/karafka-web +2 -1
  8. data/docker-compose.yml +2 -0
  9. data/karafka-web.gemspec +2 -2
  10. data/lib/karafka/web/cli/base.rb +35 -0
  11. data/lib/karafka/web/cli/help.rb +25 -0
  12. data/lib/karafka/web/cli/install.rb +27 -0
  13. data/lib/karafka/web/cli/migrate.rb +33 -0
  14. data/lib/karafka/web/cli/reset.rb +27 -0
  15. data/lib/karafka/web/cli/uninstall.rb +17 -0
  16. data/lib/karafka/web/cli.rb +9 -76
  17. data/lib/karafka/web/config.rb +36 -2
  18. data/lib/karafka/web/processing/consumers/metrics.rb +4 -1
  19. data/lib/karafka/web/processing/consumers/state.rb +4 -1
  20. data/lib/karafka/web/tracking/consumers/reporter.rb +21 -30
  21. data/lib/karafka/web/tracking/producers/reporter.rb +6 -4
  22. data/lib/karafka/web/tracking/producers/sampler.rb +3 -0
  23. data/lib/karafka/web/tracking/reporter.rb +25 -0
  24. data/lib/karafka/web/tracking/scheduler.rb +46 -0
  25. data/lib/karafka/web/ui/lib/admin.rb +56 -0
  26. data/lib/karafka/web/ui/models/cluster_info.rb +2 -2
  27. data/lib/karafka/web/ui/models/consumers_metrics.rb +4 -2
  28. data/lib/karafka/web/ui/models/consumers_state.rb +4 -2
  29. data/lib/karafka/web/ui/models/counters.rb +1 -1
  30. data/lib/karafka/web/ui/models/message.rb +2 -2
  31. data/lib/karafka/web/ui/models/processes.rb +1 -1
  32. data/lib/karafka/web/ui/models/watermark_offsets.rb +1 -1
  33. data/lib/karafka/web/ui/pro/controllers/explorer.rb +16 -5
  34. data/lib/karafka/web/ui/pro/views/errors/_error.erb +1 -1
  35. data/lib/karafka/web/ui/pro/views/explorer/_message.erb +1 -1
  36. data/lib/karafka/web/ui/public/javascripts/live_poll.js +1 -1
  37. data/lib/karafka/web/ui/views/errors/_error.erb +1 -1
  38. data/lib/karafka/web/version.rb +1 -1
  39. data.tar.gz.sig +0 -0
  40. metadata +14 -6
  41. metadata.gz.sig +0 -0
  42. data/lib/karafka/web/tracking/producers/listeners/reporter.rb +0 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 32657edc369df2240e6788c6972d0c83073263ad3e2cc416ff4d24bde1677f18
4
- data.tar.gz: d013c25e74a87d1820f60912805888c42c8432a24e1ed33674276f8647328585
3
+ metadata.gz: 2d46244304483184c3be59a0686497a9f948e9b666dccf00961b560e60db4655
4
+ data.tar.gz: 329686ce548063787fe06290d67650793ebef6c014f43b753780ad120671a8b1
5
5
  SHA512:
6
- metadata.gz: 148e9fbe63c0029b1181aece3569f2822e108240b94c78e960b7e24a2921553dc64cd5c3d59f95fe555df2265bfd216fac97ba8906dbae2cb8c8611a66963400
7
- data.tar.gz: c015c0c8cc8d15351c2620e3a4a87ee10a986277bf1f3e50fc0c7d2e9d66f839d596bf94273b067eac7fb2f1f231453ac91119ec125cea90fb8bd6eff8943333
6
+ metadata.gz: 5a18a3c23f63382c6c41dc5ac7f82f893bc14a55e7ec68af9cf16da0f47a45aee77ca9626973570cb9d6abe9355e0712f117424e00619b7b0198748c3bfbeb55
7
+ data.tar.gz: f639bd90ca21c09fcd798fc289079cafeb9c752d261ecf88df9762e347df738bf260c658f0f15b3b08c3708daf091217c0159d85ac975380463a65a45d876d0b
checksums.yaml.gz.sig CHANGED
Binary file
@@ -68,7 +68,7 @@ jobs:
68
68
  strategy:
69
69
  fail-fast: false
70
70
  steps:
71
- - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
71
+ - uses: actions/checkout@v4
72
72
  with:
73
73
  fetch-depth: 0
74
74
 
@@ -89,7 +89,7 @@ jobs:
89
89
  strategy:
90
90
  fail-fast: false
91
91
  steps:
92
- - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
92
+ - uses: actions/checkout@v4
93
93
  with:
94
94
  fetch-depth: 0
95
95
  - name: Run Coditsu
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Karafka Web changelog
2
2
 
3
+ ## 0.7.8 (2023-10-24)
4
+ - [Enhancement] Support transactional producer usage with Web UI.
5
+ - [Fix] Fix a bug where critical errors (like `IRB::Abort`) would not abort the ongoing transaction.
6
+ - [Fix] Prevent a scenario where an ongoing transactional producer would have stats emitted and an error that could not have been dispatched because of the transaction, creating a dead-lock.
7
+ - [Fix] Make sure that the `recent` displays the most recent non-compacted, non-system message.
8
+ - [Fix] Improve the `recent` message display to compensate for aborted transactions.
9
+ - [Fix] Fix `ReferenceError: response is not defined` that occurs when Web UI returns refresh non 200.
10
+
11
+ ## 0.7.7 (2023-10-20)
12
+ - [Fix] Remove `thor` as a CLI engine due to breaking changes.
13
+
3
14
  ## 0.7.6 (2023-10-10)
4
15
  - [Fix] Fix nested SASL/SAML data visible in the routing details (#173)
5
16
 
data/Gemfile.lock CHANGED
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka-web (0.7.6)
4
+ karafka-web (0.7.8)
5
5
  erubi (~> 1.4)
6
- karafka (>= 2.2.6, < 3.0.0)
6
+ karafka (>= 2.2.9, < 3.0.0)
7
7
  karafka-core (>= 2.2.2, < 3.0.0)
8
8
  roda (~> 3.68, >= 3.69)
9
9
  tilt (~> 2.0)
@@ -11,35 +11,45 @@ PATH
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- activesupport (7.0.8)
14
+ activesupport (7.1.1)
15
+ base64
16
+ bigdecimal
15
17
  concurrent-ruby (~> 1.0, >= 1.0.2)
18
+ connection_pool (>= 2.2.5)
19
+ drb
16
20
  i18n (>= 1.6, < 2)
17
21
  minitest (>= 5.1)
22
+ mutex_m
18
23
  tzinfo (~> 2.0)
24
+ base64 (0.1.1)
25
+ bigdecimal (3.1.4)
19
26
  byebug (11.1.3)
20
27
  concurrent-ruby (1.2.2)
28
+ connection_pool (2.4.1)
21
29
  diff-lcs (1.5.0)
22
30
  docile (1.4.0)
31
+ drb (2.1.1)
32
+ ruby2_keywords
23
33
  erubi (1.12.0)
24
34
  factory_bot (6.3.0)
25
35
  activesupport (>= 5.0.0)
26
- ffi (1.15.5)
36
+ ffi (1.16.3)
27
37
  i18n (1.14.1)
28
38
  concurrent-ruby (~> 1.0)
29
- karafka (2.2.6)
39
+ karafka (2.2.9)
30
40
  karafka-core (>= 2.2.2, < 2.3.0)
31
- thor (>= 0.20)
32
- waterdrop (>= 2.6.6, < 3.0.0)
41
+ waterdrop (>= 2.6.10, < 3.0.0)
33
42
  zeitwerk (~> 2.3)
34
- karafka-core (2.2.2)
43
+ karafka-core (2.2.3)
35
44
  concurrent-ruby (>= 1.1)
36
- karafka-rdkafka (>= 0.13.1, < 0.14.0)
37
- karafka-rdkafka (0.13.4)
45
+ karafka-rdkafka (>= 0.13.6, < 0.14.0)
46
+ karafka-rdkafka (0.13.6)
38
47
  ffi (~> 1.15)
39
48
  mini_portile2 (~> 2.6)
40
49
  rake (> 12)
41
- mini_portile2 (2.8.4)
50
+ mini_portile2 (2.8.5)
42
51
  minitest (5.20.0)
52
+ mutex_m (0.1.2)
43
53
  rack (3.0.8)
44
54
  rack-test (2.1.0)
45
55
  rack (>= 1.3)
@@ -47,7 +57,7 @@ GEM
47
57
  rack (>= 3.0.0.beta1)
48
58
  webrick
49
59
  rake (13.0.6)
50
- roda (3.71.0)
60
+ roda (3.73.0)
51
61
  rack
52
62
  rspec (3.12.0)
53
63
  rspec-core (~> 3.12.0)
@@ -62,21 +72,21 @@ GEM
62
72
  diff-lcs (>= 1.2.0, < 2.0)
63
73
  rspec-support (~> 3.12.0)
64
74
  rspec-support (3.12.1)
75
+ ruby2_keywords (0.0.5)
65
76
  simplecov (0.22.0)
66
77
  docile (~> 1.1)
67
78
  simplecov-html (~> 0.11)
68
79
  simplecov_json_formatter (~> 0.1)
69
80
  simplecov-html (0.12.3)
70
81
  simplecov_json_formatter (0.1.4)
71
- thor (1.2.2)
72
- tilt (2.2.0)
82
+ tilt (2.3.0)
73
83
  tzinfo (2.0.6)
74
84
  concurrent-ruby (~> 1.0)
75
- waterdrop (2.6.7)
76
- karafka-core (>= 2.1.1, < 3.0.0)
85
+ waterdrop (2.6.10)
86
+ karafka-core (>= 2.2.3, < 3.0.0)
77
87
  zeitwerk (~> 2.3)
78
88
  webrick (1.8.1)
79
- zeitwerk (2.6.11)
89
+ zeitwerk (2.6.12)
80
90
 
81
91
  PLATFORMS
82
92
  x86_64-linux
data/README.md CHANGED
@@ -8,6 +8,9 @@ Karafka Web UI is a user interface for the [Karafka framework](https://github.co
8
8
 
9
9
  It allows for easy access to various metrics, such as the number of messages consumed, the number of errors, and the number of consumers operating. It also provides a way to view the different Kafka topics, consumers, and groups that are being used by the application.
10
10
 
11
+ > [!IMPORTANT]
12
+ > All of Karafka ecosystems components documentation, including the Web UI, can be found [here](https://karafka.io/docs/#web-ui).
13
+
11
14
  ## Getting started
12
15
 
13
16
  Karafka Web UI documentation is part of the Karafka framework documentation and can be found [here](https://karafka.io/docs).
data/bin/karafka-web CHANGED
@@ -3,6 +3,7 @@
3
3
  require 'karafka'
4
4
  require 'karafka/web'
5
5
 
6
- ::Karafka::Cli::Base.load
6
+ ENV['KARAFKA_CLI'] = 'true'
7
7
 
8
+ ::Karafka::Cli::Base.load
8
9
  ::Karafka::Web::Cli.start
data/docker-compose.yml CHANGED
@@ -21,3 +21,5 @@ services:
21
21
  KAFKA_CONTROLLER_QUORUM_VOTERS: 1@127.0.0.1:9093
22
22
  ALLOW_PLAINTEXT_LISTENER: 'yes'
23
23
  KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
24
+ KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
25
+ KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
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.2.6', '< 3.0.0'
20
+ spec.add_dependency 'karafka', '>= 2.2.9', '< 3.0.0'
21
21
  spec.add_dependency 'karafka-core', '>= 2.2.2', '< 3.0.0'
22
22
  spec.add_dependency 'roda', '~> 3.68', '>= 3.69'
23
23
  spec.add_dependency 'tilt', '~> 2.0'
@@ -36,7 +36,7 @@ Gem::Specification.new do |spec|
36
36
  spec.metadata = {
37
37
  'funding_uri' => 'https://karafka.io/#become-pro',
38
38
  'homepage_uri' => 'https://karafka.io',
39
- 'changelog_uri' => 'https://github.com/karafka/karafka-web/blob/master/CHANGELOG.md',
39
+ 'changelog_uri' => 'https://karafka.io/docs/Changelog-Karafka-Web-UI',
40
40
  'bug_tracker_uri' => 'https://github.com/karafka/karafka-web/issues',
41
41
  'source_code_uri' => 'https://github.com/karafka/karafka-web',
42
42
  'documentation_uri' => 'https://karafka.io/docs',
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ class Cli
6
+ # Base command for all the Web Cli commands
7
+ class Base < Karafka::Cli::Base
8
+ include ::Karafka::Helpers::Colorize
9
+
10
+ class << self
11
+ # @return [Array<Class>] available commands
12
+ def commands
13
+ ObjectSpace
14
+ .each_object(Class)
15
+ .select { |klass| klass.superclass == Karafka::Web::Cli::Base }
16
+ .reject { |klass| klass.to_s.end_with?('::Base') }
17
+ .sort_by(&:name)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ # Takes the CLI user provided replication factor but if not present, uses the brokers count
24
+ # to decide. For non-dev clusters (with one broker) we usually want to have replication of
25
+ # two, just to have some redundancy.
26
+ # @param cli_replication_factor [Integer, false] user requested replication factor or false
27
+ # if we are supposed to compute the factor automatically
28
+ # @return [Integer] replication factor for Karafka Web UI topics
29
+ def compute_replication_factor(cli_replication_factor)
30
+ cli_replication_factor || (Ui::Models::ClusterInfo.fetch.brokers.size > 1 ? 2 : 1)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ class Cli
6
+ # Displays help
7
+ class Help < Base
8
+ desc 'Describes available commands'
9
+
10
+ # Print available commands
11
+ def call
12
+ # Find the longest command for alignment purposes
13
+ max_command_length = self.class.commands.map(&:name).map(&:size).max
14
+
15
+ puts 'Karafka Web UI commands:'
16
+
17
+ # Print each command formatted with its description
18
+ self.class.commands.each do |command|
19
+ puts " #{command.name.ljust(max_command_length)} # #{command.desc}"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ class Cli
6
+ # Installs Web UI
7
+ class Install < Base
8
+ desc 'Installs the Web UI'
9
+
10
+ option(
11
+ :replication_factor,
12
+ 'Replication factor for created topics',
13
+ Integer,
14
+ ['--replication_factor [FACTOR]']
15
+ )
16
+
17
+ # Installs Karafka Web. Creates all needed topics, populates the data and adds the needed
18
+ # code to `karafka.rb`.
19
+ def call
20
+ Karafka::Web::Installer.new.install(
21
+ replication_factor: compute_replication_factor(options[:replication_factor])
22
+ )
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ class Cli
6
+ # Migrates the Web UI topics and states if needed
7
+ class Migrate < Base
8
+ desc 'Runs necessary migrations of Web UI topics and states'
9
+
10
+ option(
11
+ :replication_factor,
12
+ 'Replication factor for created topics',
13
+ Integer,
14
+ ['--replication_factor [FACTOR]']
15
+ )
16
+
17
+ # Creates new topics (if any) and populates missing data.
18
+ # It does **not** remove topics and will not populate data if it is already there.
19
+ #
20
+ # Useful in two scenarios:
21
+ # 1. When setting up Web-UI in a new environment, so the Web-UI has the proper initial
22
+ # state.
23
+ # 2. When upgrading Web-UI in-between versions that would require extra topics and/or
24
+ # extra states populated.
25
+ def call
26
+ Karafka::Web::Installer.new.migrate(
27
+ replication_factor: compute_replication_factor(options[:replication_factor])
28
+ )
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ class Cli
6
+ # Resets the Web UI
7
+ class Reset < Base
8
+ desc 'Resets the Web UI by removing all the Web topics and creating them again'
9
+
10
+ option(
11
+ :replication_factor,
12
+ 'Replication factor for created topics',
13
+ Integer,
14
+ ['--replication_factor [FACTOR]']
15
+ )
16
+
17
+ # Resets Karafka Web. Removes the topics, creates them again and populates the initial
18
+ # state again. This is useful in case the Web-UI metrics or anything else got corrupted.
19
+ def call
20
+ Karafka::Web::Installer.new.reset(
21
+ replication_factor: compute_replication_factor(options[:replication_factor])
22
+ )
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ class Cli
6
+ # Uninstalls the Web UI
7
+ class Uninstall < Base
8
+ desc 'Removes all the Web UI topics and the enabled code'
9
+
10
+ # Uninstalls Karafka Web
11
+ def call
12
+ Karafka::Web::Installer.new.uninstall
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -2,82 +2,15 @@
2
2
 
3
3
  module Karafka
4
4
  module Web
5
- # Karafka itself depends on Thor, so we can use it
6
- class Cli < Thor
7
- include ::Karafka::Helpers::Colorize
8
-
9
- package_name 'Karafka Web'
10
-
11
- desc 'install', 'Installs the Web UI'
12
- method_option(
13
- :replication_factor,
14
- desc: 'Replication factor for created topics',
15
- default: false,
16
- check_default_type: false,
17
- type: :numeric
18
- )
19
- # Installs Karafka Web. Creates all needed topics, populates the data and adds the needed
20
- # code to `karafka.rb`.
21
- def install
22
- Karafka::Web::Installer.new.install(
23
- replication_factor: compute_replication_factor(options[:replication_factor])
24
- )
25
- end
26
-
27
- desc 'migrate', 'Creates necessary topics if not present and populates state data'
28
- method_option(
29
- :replication_factor,
30
- desc: 'Replication factor for created topics',
31
- default: false,
32
- check_default_type: false,
33
- type: :numeric
34
- )
35
- # Creates new topics (if any) and populates missing data.
36
- # It does **not** remove topics and will not populate data if it is already there.
37
- #
38
- # Useful in two scenarios:
39
- # 1. When setting up Web-UI in a new environment, so the Web-UI has the proper initial
40
- # state.
41
- # 2. When upgrading Web-UI in-between versions that would require extra topics and/or extra
42
- # states populated.
43
- def migrate
44
- Karafka::Web::Installer.new.migrate(
45
- replication_factor: compute_replication_factor(options[:replication_factor])
46
- )
47
- end
48
-
49
- desc 'reset', 'Resets the Web UI by removing all the Web topics and creating them again'
50
- method_option(
51
- :replication_factor,
52
- desc: 'Replication factor for created topics',
53
- default: false,
54
- check_default_type: false,
55
- type: :numeric
56
- )
57
- # Resets Karafka Web. Removes the topics, creates them again and populates the initial state
58
- # again. This is useful in case the Web-UI metrics or anything else got corrupted.
59
- def reset
60
- Karafka::Web::Installer.new.reset(
61
- replication_factor: compute_replication_factor(options[:replication_factor])
62
- )
63
- end
64
-
65
- desc 'uninstall', 'Removes all the Web UI topics and the enabled code'
66
- # Uninstalls Karafka Web
67
- def uninstall
68
- Karafka::Web::Installer.new.uninstall
69
- end
70
-
71
- private
72
-
73
- # Takes the CLI user provided replication factor but if not present, uses the brokers count
74
- # to decide. For non-dev clusters (with one broker) we usually want to have replication of
75
- # two, just to have some redundancy.
76
- # @param cli_replication_factor [Integer, false] user requested replication factor or false
77
- # if we are supposed to compute the factor automatically
78
- # @return [Integer] replication factor for Karafka Web UI topics
79
- def compute_replication_factor(cli_replication_factor)
80
- cli_replication_factor || Ui::Models::ClusterInfo.fetch.brokers.size > 1 ? 2 : 1
5
+ # Web CLI
6
+ class Cli < Karafka::Cli
7
+ class << self
8
+ private
9
+
10
+ # @return [Array<Class>] command classes
11
+ def commands
12
+ Base.commands
13
+ end
81
14
  end
82
15
  end
83
16
  end
@@ -36,6 +36,10 @@ module Karafka
36
36
  # 5 seconds should be enough
37
37
  setting :interval, default: 5_000
38
38
 
39
+ # Main Web UI reporting scheduler that runs a background thread and reports periodically
40
+ # from the consumer reporter and producer reporter
41
+ setting :scheduler, default: Tracking::Scheduler.new
42
+
39
43
  setting :consumers do
40
44
  # Reports the metrics collected in the sampler
41
45
  setting :reporter, default: Tracking::Consumers::Reporter.new
@@ -58,8 +62,7 @@ module Karafka
58
62
  setting :sampler, default: Tracking::Producers::Sampler.new
59
63
 
60
64
  setting :listeners, default: [
61
- Tracking::Producers::Listeners::Errors.new,
62
- Tracking::Producers::Listeners::Reporter.new
65
+ Tracking::Producers::Listeners::Errors.new
63
66
  ]
64
67
  end
65
68
  end
@@ -114,6 +117,37 @@ module Karafka
114
117
  # In some cases you may want to limit what is being displayed due to the type of data you
115
118
  # are dealing with
116
119
  setting :visibility_filter, default: Ui::Models::VisibilityFilter.new
120
+
121
+ # Specific kafka settings that are tuned to operate within the Web UI interface.
122
+ #
123
+ # Please do not change them unless you know what you are doing as their misconfiguration
124
+ # may cause Web UI to misbehave
125
+ #
126
+ # The settings are inherited as follows:
127
+ # 1. root routing level `kafka` settings
128
+ # 2. admin `kafka` settings
129
+ # 3. web ui `kafka` settings from here
130
+ #
131
+ # Those settings impact ONLY Web UI interface and do not affect other scopes. This is done
132
+ # on purpose as we want to improve responsiveness of the interface by tuning some of the
133
+ # settings and this is not that relevant for processing itself.
134
+ #
135
+ # option [Hash] extra changes to the default admin kafka settings
136
+ setting :kafka, default: {
137
+ # optimizes the responsiveness of the Web UI in three scenarios:
138
+ # - topics to which writes happen only in transactions so EOF is yield faster
139
+ # - heavily compacted topics
140
+ # - Web UI topics read operations when using transactional producer
141
+ #
142
+ # This can be configured to be higher if you do not use transactional WaterDrop producer.
143
+ # This value is used when last message (first from the high watermark offset) is the
144
+ # transaction commit message. In cases like this the EOF gets propagated after this time
145
+ # so we have to wait. Default 500ms means, that for some views, where we take our data
146
+ # that might have been committed via transactional producer, we would wait for 1 second
147
+ # to get needed data. If you are experiencing timeouts or other issues with the Web IU
148
+ # interface, you can increase this.
149
+ 'fetch.wait.max.ms': 100
150
+ }
117
151
  end
118
152
  end
119
153
  end
@@ -14,7 +14,10 @@ module Karafka
14
14
  metrics_message = ::Karafka::Admin.read_topic(
15
15
  Karafka::Web.config.topics.consumers.metrics,
16
16
  0,
17
- 1
17
+ # We need to take more in case there would be transactions running.
18
+ # In theory we could take two but this compensates for any involuntary
19
+ # revocations and cases where two producers would write to the same state
20
+ 5
18
21
  ).last
19
22
 
20
23
  return metrics_message.payload if metrics_message
@@ -14,7 +14,10 @@ module Karafka
14
14
  state_message = ::Karafka::Admin.read_topic(
15
15
  Karafka::Web.config.topics.consumers.states,
16
16
  0,
17
- 1
17
+ # We need to take more in case there would be transactions running.
18
+ # In theory we could take two but this compensates for any involuntary
19
+ # revocations and cases where two producers would write to the same state
20
+ 5
18
21
  ).last
19
22
 
20
23
  return state_message.payload if state_message
@@ -5,10 +5,7 @@ module Karafka
5
5
  module Tracking
6
6
  module Consumers
7
7
  # Reports the collected data about the process and sends it, so we can use it in the UI
8
- class Reporter
9
- include ::Karafka::Core::Helpers::Time
10
- include ::Karafka::Helpers::Async
11
-
8
+ class Reporter < Tracking::Reporter
12
9
  # Minimum number of messages to produce to produce them in sync mode
13
10
  # This acts as a small back-off not to overload the system in case we would have
14
11
  # extremely big number of errors happening
@@ -21,12 +18,31 @@ module Karafka
21
18
  MUTEX = Mutex.new
22
19
 
23
20
  def initialize
21
+ super
24
22
  # Move back so first report is dispatched fast to indicate, that the process is alive
25
23
  @tracked_at = monotonic_now - 10_000
26
24
  @report_contract = Consumers::Contracts::Report.new
27
25
  @error_contract = Tracking::Contracts::Error.new
28
26
  end
29
27
 
28
+ # We never report in initializing phase because things are not yet fully configured
29
+ # We never report in the initialized because server is not yet ready until Karafka is
30
+ # fully running and some of the things like listeners are not yet available
31
+ #
32
+ # This method will also be `false` in case we are not running in `karafka server` or
33
+ # in embedding, because in those cases Karafka does not go beyond the `initialized` phase
34
+ #
35
+ # @return [Boolean] are we able to report consumer state
36
+ def active?
37
+ # If we do not have a producer that we could use to report or it was closed, we cannot
38
+ # and should not report
39
+ return false unless super
40
+ return false if ::Karafka::App.initializing?
41
+ return false if ::Karafka::App.initialized?
42
+
43
+ true
44
+ end
45
+
30
46
  # Dispatches the current state from sampler to appropriate topics
31
47
  #
32
48
  # @param forced [Boolean] should we report bypassing the time frequency or should we
@@ -41,11 +57,6 @@ module Karafka
41
57
  sampler.sample
42
58
 
43
59
  MUTEX.synchronize do
44
- # Start background thread only when needed
45
- # This prevents us from starting it too early or for non-consumer processes where
46
- # Karafka is being included
47
- async_call unless @running
48
-
49
60
  return unless report?(forced)
50
61
 
51
62
  @tracked_at = monotonic_now
@@ -97,31 +108,11 @@ module Karafka
97
108
 
98
109
  private
99
110
 
100
- # Reports the process state once in a while
101
- def call
102
- @running = true
103
-
104
- # We won't track more often anyhow but want to try frequently not to miss a window
105
- # We need to convert the sleep interval into seconds for sleep
106
- sleep_time = ::Karafka::Web.config.tracking.interval.to_f / 1_000 / 10
107
-
108
- loop do
109
- report
110
-
111
- sleep(sleep_time)
112
- end
113
- end
114
-
115
111
  # @param forced [Boolean] is this report forced. Forced means that as long as we can
116
112
  # flush we will flush
117
113
  # @return [Boolean] Should we report or is it not yet time to do so
118
114
  def report?(forced)
119
- # We never report in initializing phase because things are not yet fully configured
120
- return false if ::Karafka::App.initializing?
121
- # We never report in the initialized because server is not yet ready until Karafka is
122
- # fully running and some of the things like listeners are not yet available
123
- return false if ::Karafka::App.initialized?
124
-
115
+ return false unless active?
125
116
  return true if forced
126
117
 
127
118
  (monotonic_now - @tracked_at) >= ::Karafka::Web.config.tracking.interval
@@ -9,9 +9,7 @@ module Karafka
9
9
  # @note Producer reported does not have to operate with the `forced` dispatch mainly
10
10
  # because there is no expectation on immediate status updates for producers and their
11
11
  # dispatch flow is always periodic based.
12
- class Reporter
13
- include ::Karafka::Core::Helpers::Time
14
-
12
+ class Reporter < Tracking::Reporter
15
13
  # Minimum number of messages to produce to produce them in sync mode
16
14
  # This acts as a small back-off not to overload the system in case we would have
17
15
  # extremely big number of errors happening
@@ -24,6 +22,7 @@ module Karafka
24
22
  MUTEX = Mutex.new
25
23
 
26
24
  def initialize
25
+ super
27
26
  # If there are any errors right after we started sampling, dispatch them immediately
28
27
  @tracked_at = monotonic_now - 10_000
29
28
  @error_contract = Tracking::Contracts::Error.new
@@ -62,7 +61,7 @@ module Karafka
62
61
 
63
62
  # @return [Boolean] Should we report or is it not yet time to do so
64
63
  def report?
65
- return false unless ::Karafka.producer.status.active?
64
+ return false unless active?
66
65
 
67
66
  (monotonic_now - @tracked_at) >= ::Karafka::Web.config.tracking.interval
68
67
  end
@@ -93,6 +92,9 @@ module Karafka
93
92
  # and we can just safely ignore this
94
93
  rescue WaterDrop::Errors::ProducerClosedError
95
94
  nil
95
+ rescue StandardError => e
96
+ p '------------------------------------------------'
97
+ p e
96
98
  end
97
99
  end
98
100
  end
@@ -26,6 +26,9 @@ module Karafka
26
26
  # We cannot report and track the same time, that is why we use mutex here. To make sure
27
27
  # that samples aggregations and counting does not interact with reporter flushing.
28
28
  def track
29
+ # Prevents deadlocks when something producer related fails in the Web UI reporter
30
+ return yield(self) if Reporter::MUTEX.owned?
31
+
29
32
  Reporter::MUTEX.synchronize do
30
33
  yield(self)
31
34
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ module Tracking
6
+ # Base reporter from which all the reports should inherit
7
+ class Reporter
8
+ include ::Karafka::Core::Helpers::Time
9
+
10
+ # Can this reporter report. Since some reporters may report only in part of the processes
11
+ # where Karafka is used (like `karafka server`) each may implement more complex rules.
12
+ #
13
+ # The basic is not to report unless we have a producer and this producer is active
14
+ #
15
+ # @return [Boolean]
16
+ def active?
17
+ return false unless ::Karafka::App.producer
18
+ return false unless ::Karafka::App.producer.status.active?
19
+
20
+ true
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ module Tracking
6
+ # Triggers reporters to report in an async mode in a separate thread
7
+ # We report this way to prevent any potential dead-locks in cases we would be emitting
8
+ # statistics during transactions.
9
+ #
10
+ # We should never use the notifications thread for sensitive IO bound operations.
11
+ class Scheduler
12
+ include ::Karafka::Helpers::Async
13
+
14
+ # Creates the scheduler and runs its internal reporting
15
+ def initialize
16
+ async_call
17
+ end
18
+
19
+ private
20
+
21
+ # Reports the process state once in a while
22
+ def call
23
+ # We won't track more often anyhow but want to try frequently not to miss a window
24
+ # We need to convert the sleep interval into seconds for sleep
25
+ sleep_time = ::Karafka::Web.config.tracking.interval.to_f / 1_000 / 10
26
+
27
+ loop do
28
+ # Not every reporter may be active at a given stage or in a context of a given process
29
+ # We select only those that decided that they are active.
30
+ reporters.select(&:active?).each(&:report)
31
+
32
+ sleep(sleep_time)
33
+ end
34
+ end
35
+
36
+ # @return [Array] consumers and producers reporters
37
+ def reporters
38
+ @reporters ||= [
39
+ ::Karafka::Web.config.tracking.consumers.reporter,
40
+ ::Karafka::Web.config.tracking.producers.reporter
41
+ ].freeze
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ module Ui
6
+ module Lib
7
+ # Wrapper around Karafka Admin that alters its behaviours or injects Web UI interface
8
+ # specific settings that optimize the responsiveness of the UI when operating on topics
9
+ #
10
+ # @note Not all commands need those optimizations, hence we alter only those that need
11
+ # that and we only expose those admin commands that are used in the Web-UI interface
12
+ # component.
13
+ #
14
+ # @note We expose here only admin methods used in the Web UI interface. Processing uses the
15
+ # `Karafka::Admin` with the defaults
16
+ class Admin
17
+ class << self
18
+ extend Forwardable
19
+
20
+ def_delegators ::Karafka::Admin, :read_watermark_offsets, :cluster_info
21
+
22
+ # Allows us to read messages from the topic
23
+ #
24
+ # @param name [String, Symbol] topic name
25
+ # @param partition [Integer] partition
26
+ # @param count [Integer] how many messages we want to get at most
27
+ # @param start_offset [Integer, Time] offset from which we should start. If -1 is provided
28
+ # (default) we will start from the latest offset. If time is provided, the appropriate
29
+ # offset will be resolved. If negative beyond -1 is provided, we move backwards more.
30
+ # @param settings [Hash] kafka extra settings (optional)
31
+ #
32
+ # @return [Array<Karafka::Messages::Message>] array with messages
33
+ def read_topic(name, partition, count, start_offset = -1, settings = {})
34
+ ::Karafka::Admin.read_topic(
35
+ name,
36
+ partition,
37
+ count,
38
+ start_offset,
39
+ # Merge our Web UI specific settings
40
+ config.merge(settings)
41
+ )
42
+ end
43
+
44
+ private
45
+
46
+ # @return [Hash] kafka config for Web UI interface.
47
+ # @note It does **not** affect tracking or processing
48
+ def config
49
+ ::Karafka::Web.config.ui.kafka
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -4,7 +4,7 @@ module Karafka
4
4
  module Web
5
5
  module Ui
6
6
  module Models
7
- # Wraps around the `Karafka::Admin#cluster_info` with caching and some additional aliases
7
+ # Wraps around the `Lib::Admin#cluster_info` with caching and some additional aliases
8
8
  # so we can reference relevant information easily
9
9
  class ClusterInfo
10
10
  class << self
@@ -18,7 +18,7 @@ module Karafka
18
18
  cluster_info = cache.read(:cluster_info)
19
19
 
20
20
  if cluster_info.nil? || !cached
21
- cluster_info = cache.write(:cluster_info, Karafka::Admin.cluster_info)
21
+ cluster_info = cache.write(:cluster_info, Lib::Admin.cluster_info)
22
22
  end
23
23
 
24
24
  cluster_info
@@ -32,10 +32,12 @@ module Karafka
32
32
 
33
33
  # @return [::Karafka::Messages::Message, nil] most recent state or nil if none
34
34
  def fetch
35
- ::Karafka::Admin.read_topic(
35
+ Lib::Admin.read_topic(
36
36
  Karafka::Web.config.topics.consumers.metrics,
37
37
  0,
38
- 1
38
+ # We need to take last two and not the last because in case of a transactional
39
+ # producer, the last one will match the transaction commit message
40
+ 2
39
41
  ).last
40
42
  end
41
43
  end
@@ -41,10 +41,12 @@ module Karafka
41
41
 
42
42
  # @return [::Karafka::Messages::Message, nil] most recent state or nil if none
43
43
  def fetch
44
- ::Karafka::Admin.read_topic(
44
+ Lib::Admin.read_topic(
45
45
  Karafka::Web.config.topics.consumers.states,
46
46
  0,
47
- 1
47
+ # We need to take last two and not the last because in case of a transactional
48
+ # producer, the last one will match the transaction commit message
49
+ 2
48
50
  ).last
49
51
  end
50
52
 
@@ -25,7 +25,7 @@ module Karafka
25
25
 
26
26
  MAX_ERROR_PARTITIONS.times do |partition|
27
27
  begin
28
- offsets = Karafka::Admin.read_watermark_offsets(
28
+ offsets = Lib::Admin.read_watermark_offsets(
29
29
  ::Karafka::Web.config.topics.errors,
30
30
  partition
31
31
  )
@@ -17,7 +17,7 @@ module Karafka
17
17
  # @param offset [Integer]
18
18
  # @raise [::Karafka::Web::Errors::Ui::NotFoundError] when not found
19
19
  def find(topic_id, partition_id, offset)
20
- message = Karafka::Admin.read_topic(
20
+ message = Lib::Admin.read_topic(
21
21
  topic_id,
22
22
  partition_id,
23
23
  1,
@@ -195,7 +195,7 @@ module Karafka
195
195
  # @return [Array<Karafka::Messages::Message>, false] topic partition messages or false
196
196
  # in case we hit a non-existing offset
197
197
  def read_topic(*args)
198
- ::Karafka::Admin.read_topic(*args)
198
+ Lib::Admin.read_topic(*args)
199
199
  rescue Rdkafka::RdkafkaError => e
200
200
  return false if e.code == :auto_offset_reset
201
201
 
@@ -44,7 +44,7 @@ module Karafka
44
44
  .map { |process| process[:offset] }
45
45
  .sort
46
46
 
47
- ::Karafka::Admin.read_topic(
47
+ Lib::Admin.read_topic(
48
48
  ::Karafka::Web.config.topics.consumers.reports,
49
49
  0,
50
50
  # We set 10k here because we start from the latest offset of the reports, hence
@@ -13,7 +13,7 @@ module Karafka
13
13
  # @param partition_id [Integer]
14
14
  # @return [WatermarkOffsets]
15
15
  def find(topic_id, partition_id)
16
- offsets = ::Karafka::Admin.read_watermark_offsets(topic_id, partition_id)
16
+ offsets = Lib::Admin.read_watermark_offsets(topic_id, partition_id)
17
17
 
18
18
  new(
19
19
  low: offsets.first,
@@ -132,11 +132,22 @@ module Karafka
132
132
  active_partitions, = Paginators::Partitions.call(partitions_count, 1)
133
133
  end
134
134
 
135
- # This selects first page with most recent messages
136
- messages, = Models::Message.topic_page(topic_id, active_partitions, 1)
135
+ recent = nil
137
136
 
138
- # Selects newest out of all partitions
139
- recent = messages.max_by(&:timestamp)
137
+ # This selects first pages with most recent messages and moves to next if first
138
+ # contains only compacted data, etc.
139
+ #
140
+ # We do it until we find a message we could refer to (if doable) within first
141
+ # ten pages
142
+ 10.times do |page|
143
+ messages, = Models::Message.topic_page(topic_id, active_partitions, page + 1)
144
+
145
+ # Selects newest out of all partitions
146
+ # Reject compacted messages and transaction-related once
147
+ recent = messages.reject { |message| message.is_a?(Array) }.max_by(&:timestamp)
148
+
149
+ break if recent
150
+ end
140
151
 
141
152
  recent || raise(::Karafka::Web::Errors::Ui::NotFoundError)
142
153
 
@@ -184,7 +195,7 @@ module Karafka
184
195
  # @param partition_id [Integer]
185
196
  # @param time [Time] time of the message
186
197
  def closest(topic_id, partition_id, time)
187
- target = ::Karafka::Admin.read_topic(topic_id, partition_id, 1, time).first
198
+ target = Lib::Admin.read_topic(topic_id, partition_id, 1, time).first
188
199
 
189
200
  partition_path = "explorer/#{topic_id}/#{partition_id}"
190
201
  partition_path += "?offset=#{target.offset}" if target
@@ -1,7 +1,7 @@
1
1
  <% if error_msg.is_a?(Array) %>
2
2
  <tr>
3
3
  <td colspan="5" class="text-center text-muted">
4
- This error has either been removed or compacted and is no longer available.
4
+ This offset does not contain error data. The message may have been compacted or is a system entry.
5
5
  </td>
6
6
  </tr>
7
7
  <% else %>
@@ -7,7 +7,7 @@
7
7
  <%= message[1] %>
8
8
  </td>
9
9
  <td colspan="3" class="text-center text-muted">
10
- This message has either been removed or compacted and is no longer available.
10
+ This offset does not contain any data. The message may have been compacted or is a system entry.
11
11
  </td>
12
12
  </tr>
13
13
  <% else %>
@@ -73,7 +73,7 @@ function setLivePollButton() {
73
73
 
74
74
  function checkResponse(resp) {
75
75
  if (!resp.ok) {
76
- throw response.error();
76
+ throw resp;
77
77
  }
78
78
  return resp
79
79
  }
@@ -1,7 +1,7 @@
1
1
  <% if error_msg.is_a?(Array) %>
2
2
  <tr>
3
3
  <td colspan="5" class="text-center text-muted">
4
- This error has either been removed or compacted and is no longer available.
4
+ This offset does not contain error data. The message may have been compacted or is a system entry.
5
5
  </td>
6
6
  </tr>
7
7
  <% else %>
@@ -3,6 +3,6 @@
3
3
  module Karafka
4
4
  module Web
5
5
  # Current gem version
6
- VERSION = '0.7.6'
6
+ VERSION = '0.7.8'
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.7.6
4
+ version: 0.7.8
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: 2023-10-10 00:00:00.000000000 Z
38
+ date: 2023-10-24 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.2.6
60
+ version: 2.2.9
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.2.6
70
+ version: 2.2.9
71
71
  - - "<"
72
72
  - !ruby/object:Gem::Version
73
73
  version: 3.0.0
@@ -173,6 +173,12 @@ files:
173
173
  - lib/karafka/web.rb
174
174
  - lib/karafka/web/app.rb
175
175
  - lib/karafka/web/cli.rb
176
+ - lib/karafka/web/cli/base.rb
177
+ - lib/karafka/web/cli/help.rb
178
+ - lib/karafka/web/cli/install.rb
179
+ - lib/karafka/web/cli/migrate.rb
180
+ - lib/karafka/web/cli/reset.rb
181
+ - lib/karafka/web/cli/uninstall.rb
176
182
  - lib/karafka/web/config.rb
177
183
  - lib/karafka/web/contracts/base.rb
178
184
  - lib/karafka/web/contracts/config.rb
@@ -219,10 +225,11 @@ files:
219
225
  - lib/karafka/web/tracking/memoized_shell.rb
220
226
  - lib/karafka/web/tracking/producers/listeners/base.rb
221
227
  - lib/karafka/web/tracking/producers/listeners/errors.rb
222
- - lib/karafka/web/tracking/producers/listeners/reporter.rb
223
228
  - lib/karafka/web/tracking/producers/reporter.rb
224
229
  - lib/karafka/web/tracking/producers/sampler.rb
230
+ - lib/karafka/web/tracking/reporter.rb
225
231
  - lib/karafka/web/tracking/sampler.rb
232
+ - lib/karafka/web/tracking/scheduler.rb
226
233
  - lib/karafka/web/tracking/ttl_array.rb
227
234
  - lib/karafka/web/tracking/ttl_hash.rb
228
235
  - lib/karafka/web/ui/app.rb
@@ -241,6 +248,7 @@ files:
241
248
  - lib/karafka/web/ui/controllers/status.rb
242
249
  - lib/karafka/web/ui/helpers/application_helper.rb
243
250
  - lib/karafka/web/ui/helpers/paths_helper.rb
251
+ - lib/karafka/web/ui/lib/admin.rb
244
252
  - lib/karafka/web/ui/lib/hash_proxy.rb
245
253
  - lib/karafka/web/ui/lib/paginations/base.rb
246
254
  - lib/karafka/web/ui/lib/paginations/offset_based.rb
@@ -447,7 +455,7 @@ licenses:
447
455
  metadata:
448
456
  funding_uri: https://karafka.io/#become-pro
449
457
  homepage_uri: https://karafka.io
450
- changelog_uri: https://github.com/karafka/karafka-web/blob/master/CHANGELOG.md
458
+ changelog_uri: https://karafka.io/docs/Changelog-Karafka-Web-UI
451
459
  bug_tracker_uri: https://github.com/karafka/karafka-web/issues
452
460
  source_code_uri: https://github.com/karafka/karafka-web
453
461
  documentation_uri: https://karafka.io/docs
metadata.gz.sig CHANGED
Binary file
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Karafka
4
- module Web
5
- module Tracking
6
- module Producers
7
- module Listeners
8
- # Special listener that we use to report data about producers states
9
- # We don't have to have a separate thread for reporting, because producers have their
10
- # own internal threads for changes polling and we can utilize this thread
11
- class Reporter < Base
12
- # @param _event [Karafka::Core::Monitoring::Event]
13
- def on_statistics_emitted(_event)
14
- reporter.report
15
- end
16
- end
17
- end
18
- end
19
- end
20
- end
21
- end