karafka 2.2.7 → 2.2.8.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +3 -5
- data/bin/karafka +2 -3
- data/docker-compose.yml +3 -1
- data/karafka.gemspec +1 -2
- data/lib/karafka/base_consumer.rb +1 -0
- data/lib/karafka/cli/base.rb +45 -34
- data/lib/karafka/cli/console.rb +5 -4
- data/lib/karafka/cli/help.rb +24 -0
- data/lib/karafka/cli/info.rb +2 -2
- data/lib/karafka/cli/install.rb +4 -4
- data/lib/karafka/cli/server.rb +68 -33
- data/lib/karafka/cli/topics.rb +1 -1
- data/lib/karafka/cli.rb +23 -19
- data/lib/karafka/connection/client.rb +9 -4
- data/lib/karafka/connection/rebalance_manager.rb +36 -21
- data/lib/karafka/errors.rb +3 -0
- data/lib/karafka/instrumentation/callbacks/rebalance.rb +64 -0
- data/lib/karafka/instrumentation/notifications.rb +5 -1
- data/lib/karafka/instrumentation/vendors/appsignal/base.rb +30 -0
- data/lib/karafka/instrumentation/vendors/appsignal/client.rb +122 -0
- data/lib/karafka/instrumentation/vendors/appsignal/dashboard.json +222 -0
- data/lib/karafka/instrumentation/vendors/appsignal/errors_listener.rb +30 -0
- data/lib/karafka/instrumentation/vendors/appsignal/metrics_listener.rb +331 -0
- data/lib/karafka/instrumentation/vendors/datadog/metrics_listener.rb +2 -2
- data/lib/karafka/patches/rdkafka/bindings.rb +22 -39
- data/lib/karafka/patches/rdkafka/opaque.rb +36 -0
- data/lib/karafka/pro/processing/coordinator.rb +6 -7
- data/lib/karafka/pro/processing/strategies/vp/default.rb +20 -0
- data/lib/karafka/version.rb +1 -1
- data/lib/karafka.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +14 -20
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: facd28537bbd9b1f5a7e6956c82b704a97efafdfdcea5ed9959991f129ba2ed4
|
4
|
+
data.tar.gz: 4b6ca5058a07fadf7c95fa96eb08ff7820b44d4bafc43ebdaf0a7fe16cb35fd2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd8cda16faa8d1267b934d2ccc189e6981c21be8cc4fed60042776a6370c93481b99981117d2572a52ada12ea9268877e339a5007284aa2413135ace5a078241
|
7
|
+
data.tar.gz: e765bb3a3c6de52bd6dbd12a7268d18c03cb1454068dff19ebc49d1c5a7850058b8b0853e744c6a16f07bc7f3883bf8681df19a10c0babc70defca1dbdee2b19
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# Karafka framework changelog
|
2
2
|
|
3
|
+
## 2.2.8 (Unreleased)
|
4
|
+
- **[Feature]** Introduce Appsignal integration for errors and metrics tracking.
|
5
|
+
- [Improvement] Expose `#synchronize` for VPs to allow for locks when cross-VP consumers work is needed.
|
6
|
+
- [Improvement] Provide `#collapse_until!` direct consumer API to allow for collapsed virtual partitions consumer operations together with the Filtering API for advanced use-cases.
|
7
|
+
- [Refactor] Reorganize how rebalance events are propagated from `librdkafka` to Karafka. Replace `connection.client.rebalance_callback` with `rebalance.partitions_assigned` and `rebalance.partitions_revoked`. Introduce two extra events: `rebalance.partitions_assign` and `rebalance.partitions_revoke` to handle pre-rebalance future work.
|
8
|
+
- [Refactor] Remove `thor` as a CLI layer and rely on Ruby `OptParser`
|
9
|
+
|
10
|
+
### Upgrade notes
|
11
|
+
|
12
|
+
1. Unless you were using `connection.client.rebalance_callback` which was considered private, nothing.
|
13
|
+
2. None of the CLI commands should change but `thor` has been removed so please report if you find any bugs.
|
14
|
+
|
3
15
|
## 2.2.7 (2023-10-07)
|
4
16
|
- **[Feature]** Introduce Inline Insights to both OSS and Pro. Inline Insights allow you to get the Kafka insights/metrics from the consumer instance and use them to alter the processing flow. In Pro, there's an extra filter flow allowing to ensure, that the insights exist during consumption.
|
5
17
|
- [Enhancement] Make sure, that subscription groups ids are unique by including their consumer group id in them similar to how topics ids are handled (not a breaking change).
|
data/Gemfile.lock
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
karafka (2.2.
|
4
|
+
karafka (2.2.8.beta1)
|
5
5
|
karafka-core (>= 2.2.2, < 2.3.0)
|
6
|
-
thor (>= 0.20)
|
7
6
|
waterdrop (>= 2.6.6, < 3.0.0)
|
8
7
|
zeitwerk (~> 2.3)
|
9
8
|
|
@@ -37,7 +36,7 @@ GEM
|
|
37
36
|
ffi (~> 1.15)
|
38
37
|
mini_portile2 (~> 2.6)
|
39
38
|
rake (> 12)
|
40
|
-
karafka-web (0.7.
|
39
|
+
karafka-web (0.7.6)
|
41
40
|
erubi (~> 1.4)
|
42
41
|
karafka (>= 2.2.6, < 3.0.0)
|
43
42
|
karafka-core (>= 2.2.2, < 3.0.0)
|
@@ -68,8 +67,7 @@ GEM
|
|
68
67
|
simplecov_json_formatter (~> 0.1)
|
69
68
|
simplecov-html (0.12.3)
|
70
69
|
simplecov_json_formatter (0.1.4)
|
71
|
-
|
72
|
-
tilt (2.2.0)
|
70
|
+
tilt (2.3.0)
|
73
71
|
tzinfo (2.0.6)
|
74
72
|
concurrent-ruby (~> 1.0)
|
75
73
|
waterdrop (2.6.7)
|
data/bin/karafka
CHANGED
data/docker-compose.yml
CHANGED
@@ -3,7 +3,7 @@ version: '2'
|
|
3
3
|
services:
|
4
4
|
kafka:
|
5
5
|
container_name: kafka
|
6
|
-
image: confluentinc/cp-kafka:7.5.
|
6
|
+
image: confluentinc/cp-kafka:7.5.1
|
7
7
|
|
8
8
|
ports:
|
9
9
|
- 9092:9092
|
@@ -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.gemspec
CHANGED
@@ -22,7 +22,6 @@ Gem::Specification.new do |spec|
|
|
22
22
|
DESC
|
23
23
|
|
24
24
|
spec.add_dependency 'karafka-core', '>= 2.2.2', '< 2.3.0'
|
25
|
-
spec.add_dependency 'thor', '>= 0.20'
|
26
25
|
spec.add_dependency 'waterdrop', '>= 2.6.6', '< 3.0.0'
|
27
26
|
spec.add_dependency 'zeitwerk', '~> 2.3'
|
28
27
|
|
@@ -38,7 +37,7 @@ Gem::Specification.new do |spec|
|
|
38
37
|
spec.metadata = {
|
39
38
|
'funding_uri' => 'https://karafka.io/#become-pro',
|
40
39
|
'homepage_uri' => 'https://karafka.io',
|
41
|
-
'changelog_uri' => 'https://
|
40
|
+
'changelog_uri' => 'https://karafka.io/docs/Changelog-Karafka',
|
42
41
|
'bug_tracker_uri' => 'https://github.com/karafka/karafka/issues',
|
43
42
|
'source_code_uri' => 'https://github.com/karafka/karafka',
|
44
43
|
'documentation_uri' => 'https://karafka.io/docs',
|
@@ -73,6 +73,7 @@ module Karafka
|
|
73
73
|
# @private
|
74
74
|
#
|
75
75
|
# @return [Boolean] true if there was no exception, otherwise false.
|
76
|
+
#
|
76
77
|
# @note We keep the seek offset tracking, and use it to compensate for async offset flushing
|
77
78
|
# that may not yet kick in when error occurs. That way we pause always on the last processed
|
78
79
|
# message.
|
data/lib/karafka/cli/base.rb
CHANGED
@@ -1,31 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Karafka
|
4
|
-
class Cli
|
4
|
+
class Cli
|
5
5
|
# Base class for all the command that we want to define
|
6
|
-
# This base class provides
|
7
|
-
# independent commands
|
8
|
-
# In order to define a new command you need to:
|
9
|
-
# - specify its desc
|
10
|
-
# - implement call method
|
11
|
-
#
|
12
|
-
# @example Create a dummy command
|
13
|
-
# class Dummy < Base
|
14
|
-
# self.desc = 'Dummy command'
|
15
|
-
#
|
16
|
-
# def call
|
17
|
-
# puts 'I'm doing nothing!
|
18
|
-
# end
|
19
|
-
# end
|
6
|
+
# This base class provides an interface to easier separate single independent commands
|
20
7
|
class Base
|
21
|
-
|
8
|
+
# @return [Hash] given command cli options
|
9
|
+
attr_reader :options
|
22
10
|
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
def initialize(cli)
|
28
|
-
@cli = cli
|
11
|
+
# Creates new CLI command instance
|
12
|
+
def initialize
|
13
|
+
# Parses the given command CLI options
|
14
|
+
@options = self.class.parse_options
|
29
15
|
end
|
30
16
|
|
31
17
|
# This method should implement proper cli action
|
@@ -64,26 +50,46 @@ module Karafka
|
|
64
50
|
|
65
51
|
# Allows to set description of a given cli command
|
66
52
|
# @param desc [String] Description of a given cli command
|
67
|
-
def desc(desc)
|
53
|
+
def desc(desc = nil)
|
68
54
|
@desc ||= desc
|
69
55
|
end
|
70
56
|
|
71
|
-
#
|
72
|
-
#
|
73
|
-
|
74
|
-
|
75
|
-
|
57
|
+
# Allows to set aliases for a given cli command
|
58
|
+
# @param args [Array] list of aliases that we can use to run given cli command
|
59
|
+
def aliases(*args)
|
60
|
+
@aliases ||= []
|
61
|
+
@aliases << args.map(&:to_s)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Parses the CLI options
|
65
|
+
# @return [Hash] hash with parsed values
|
66
|
+
def parse_options
|
67
|
+
options = {}
|
76
68
|
|
77
|
-
|
69
|
+
OptionParser.new do |opts|
|
70
|
+
(@options || []).each do |option|
|
71
|
+
# Creates aliases for backwards compatibility
|
72
|
+
names = option[3].flat_map { |name| [name, name.tr('_', '-')] }
|
73
|
+
names.map! { |name| "#{name} value1,value2,valueN" } if option[2] == Array
|
74
|
+
names.uniq!
|
78
75
|
|
79
|
-
|
76
|
+
opts.on(
|
77
|
+
*[names, option[2], option[1]].flatten
|
78
|
+
) { |value| options[option[0]] = value }
|
79
|
+
end
|
80
|
+
end.parse!
|
80
81
|
|
81
|
-
|
82
|
-
context.new(self).call(*args)
|
83
|
-
end
|
82
|
+
options
|
84
83
|
end
|
85
84
|
|
86
|
-
|
85
|
+
# @return [Array<Class>] available commands
|
86
|
+
def commands
|
87
|
+
ObjectSpace
|
88
|
+
.each_object(Class)
|
89
|
+
.select { |klass| klass.superclass == Karafka::Cli::Base }
|
90
|
+
.reject { |klass| klass.to_s.end_with?('::Base') }
|
91
|
+
.sort_by(&:name)
|
92
|
+
end
|
87
93
|
|
88
94
|
# @return [String] downcased current class name that we use to define name for
|
89
95
|
# given Cli command
|
@@ -92,6 +98,11 @@ module Karafka
|
|
92
98
|
def name
|
93
99
|
to_s.split('::').last.downcase
|
94
100
|
end
|
101
|
+
|
102
|
+
# @return [Array<String>] names and aliases for command matching
|
103
|
+
def names
|
104
|
+
((@aliases || []) << name).flatten.map(&:to_s)
|
105
|
+
end
|
95
106
|
end
|
96
107
|
end
|
97
108
|
end
|
data/lib/karafka/cli/console.rb
CHANGED
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
module Karafka
|
4
4
|
# Karafka framework Cli
|
5
|
-
class Cli
|
5
|
+
class Cli
|
6
6
|
# Console Karafka Cli action
|
7
7
|
class Console < Base
|
8
|
-
desc '
|
9
|
-
|
8
|
+
desc 'Starts the Karafka console (short-cut alias: "c")'
|
9
|
+
|
10
|
+
aliases :c
|
10
11
|
|
11
12
|
class << self
|
12
13
|
# @return [String] Console executing command for non-Rails setup
|
@@ -25,7 +26,7 @@ module Karafka
|
|
25
26
|
|
26
27
|
# Start the Karafka console
|
27
28
|
def call
|
28
|
-
|
29
|
+
Info.new.call
|
29
30
|
|
30
31
|
command = ::Karafka.rails? ? self.class.rails_console : self.class.console
|
31
32
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
# Karafka framework Cli
|
5
|
+
class Cli
|
6
|
+
# Prints info with list of commands available
|
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 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
|
data/lib/karafka/cli/info.rb
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
module Karafka
|
4
4
|
# Karafka framework Cli
|
5
|
-
class Cli
|
5
|
+
class Cli
|
6
6
|
# Info Karafka Cli action
|
7
7
|
class Info < Base
|
8
|
-
desc '
|
8
|
+
desc 'Prints configuration details and other options of your application'
|
9
9
|
|
10
10
|
# Nice karafka banner
|
11
11
|
BANNER = <<~BANNER
|
data/lib/karafka/cli/install.rb
CHANGED
@@ -4,12 +4,12 @@ require 'erb'
|
|
4
4
|
|
5
5
|
module Karafka
|
6
6
|
# Karafka framework Cli
|
7
|
-
class Cli
|
7
|
+
class Cli
|
8
8
|
# Install Karafka Cli action
|
9
9
|
class Install < Base
|
10
10
|
include Helpers::Colorize
|
11
11
|
|
12
|
-
desc '
|
12
|
+
desc 'Installs all required things for Karafka application in current directory'
|
13
13
|
|
14
14
|
# Directories created by default
|
15
15
|
INSTALL_DIRS = %w[
|
@@ -26,9 +26,9 @@ module Karafka
|
|
26
26
|
'example_consumer.rb.erb' => 'app/consumers/example_consumer.rb'
|
27
27
|
}.freeze
|
28
28
|
|
29
|
-
|
30
|
-
def initialize(*args)
|
29
|
+
def initialize
|
31
30
|
super
|
31
|
+
|
32
32
|
dependencies = Bundler::LockfileParser.new(
|
33
33
|
Bundler.read_file(
|
34
34
|
Bundler.default_lockfile
|
data/lib/karafka/cli/server.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Karafka
|
4
4
|
# Karafka framework Cli
|
5
|
-
class Cli
|
5
|
+
class Cli
|
6
6
|
# Server Karafka Cli action
|
7
7
|
class Server < Base
|
8
8
|
include Helpers::Colorize
|
@@ -12,36 +12,75 @@ module Karafka
|
|
12
12
|
|
13
13
|
private_constant :SUPPORTED_TYPES
|
14
14
|
|
15
|
-
desc '
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
15
|
+
desc 'Starts the Karafka server (short-cut alias: "s")'
|
16
|
+
|
17
|
+
aliases :s
|
18
|
+
|
19
|
+
option(
|
20
|
+
:consumer_groups,
|
21
|
+
'Runs server only with specified consumer groups',
|
22
|
+
Array,
|
23
|
+
%w[
|
24
|
+
-g
|
25
|
+
--consumer_groups
|
26
|
+
--include_consumer_groups
|
27
|
+
]
|
28
|
+
)
|
29
|
+
|
30
|
+
option(
|
31
|
+
:subscription_groups,
|
32
|
+
'Runs server only with specified subscription groups',
|
33
|
+
Array,
|
34
|
+
%w[
|
35
|
+
--subscription_groups
|
36
|
+
--include_subscription_groups
|
37
|
+
]
|
38
|
+
)
|
39
|
+
|
40
|
+
option(
|
41
|
+
:topics,
|
42
|
+
'Runs server only with specified topics',
|
43
|
+
Array,
|
44
|
+
%w[
|
45
|
+
--topics
|
46
|
+
--include_topics
|
47
|
+
]
|
48
|
+
)
|
49
|
+
|
50
|
+
option(
|
51
|
+
:exclude_consumer_groups,
|
52
|
+
'Runs server without specified consumer groups',
|
53
|
+
Array,
|
54
|
+
%w[
|
55
|
+
--exclude_consumer_groups
|
56
|
+
]
|
57
|
+
)
|
58
|
+
|
59
|
+
option(
|
60
|
+
:exclude_subscription_groups,
|
61
|
+
'Runs server without specified subscription groups',
|
62
|
+
Array,
|
63
|
+
%w[
|
64
|
+
--exclude_subscription_groups
|
65
|
+
]
|
66
|
+
)
|
67
|
+
|
68
|
+
option(
|
69
|
+
:exclude_topics,
|
70
|
+
'Runs server without specified topics',
|
71
|
+
Array,
|
72
|
+
%w[
|
73
|
+
--exclude_topics
|
74
|
+
]
|
75
|
+
)
|
37
76
|
|
38
77
|
# Start the Karafka server
|
39
78
|
def call
|
40
79
|
# Print our banner and info in the dev mode
|
41
80
|
print_marketing_info if Karafka::App.env.development?
|
42
81
|
|
43
|
-
register_inclusions
|
44
|
-
register_exclusions
|
82
|
+
register_inclusions
|
83
|
+
register_exclusions
|
45
84
|
|
46
85
|
Karafka::Server.run
|
47
86
|
end
|
@@ -49,26 +88,22 @@ module Karafka
|
|
49
88
|
private
|
50
89
|
|
51
90
|
# Registers things we want to include (if defined)
|
52
|
-
|
53
|
-
def register_inclusions(cli)
|
91
|
+
def register_inclusions
|
54
92
|
activities = ::Karafka::App.config.internal.routing.activity_manager
|
55
93
|
|
56
94
|
SUPPORTED_TYPES.each do |type|
|
57
|
-
|
58
|
-
v2 = cli.options[:"include_#{type}"] || []
|
59
|
-
names = v1 + v2
|
95
|
+
names = options[type] || []
|
60
96
|
|
61
97
|
names.each { |name| activities.include(type, name) }
|
62
98
|
end
|
63
99
|
end
|
64
100
|
|
65
101
|
# Registers things we want to exclude (if defined)
|
66
|
-
|
67
|
-
def register_exclusions(cli)
|
102
|
+
def register_exclusions
|
68
103
|
activities = ::Karafka::App.config.internal.routing.activity_manager
|
69
104
|
|
70
105
|
activities.class::SUPPORTED_TYPES.each do |type|
|
71
|
-
names =
|
106
|
+
names = options[:"exclude_#{type}"] || []
|
72
107
|
|
73
108
|
names.each { |name| activities.exclude(type, name) }
|
74
109
|
end
|
data/lib/karafka/cli/topics.rb
CHANGED
data/lib/karafka/cli.rb
CHANGED
@@ -5,31 +5,35 @@ module Karafka
|
|
5
5
|
#
|
6
6
|
# If you want to add/modify command that belongs to CLI, please review all commands
|
7
7
|
# available in cli/ directory inside Karafka source code.
|
8
|
-
|
9
|
-
# @note Whole Cli is built using Thor
|
10
|
-
# @see https://github.com/erikhuda/thor
|
11
|
-
class Cli < Thor
|
12
|
-
package_name 'Karafka'
|
13
|
-
|
8
|
+
class Cli
|
14
9
|
class << self
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
10
|
+
# Starts the CLI
|
11
|
+
def start
|
12
|
+
# Command we want to run, like install, server, etc
|
13
|
+
command_name = ARGV[0]
|
14
|
+
# Action for action-based commands like topics migrate
|
15
|
+
action = ARGV[1].to_s.start_with?('-') ? false : ARGV[1]
|
16
|
+
|
17
|
+
command = commands.find { |cmd| cmd.names.include?(command_name) }
|
18
|
+
|
19
|
+
if command
|
20
|
+
# Only actionable commands require command as an argument
|
21
|
+
args = action ? [action] : []
|
22
|
+
|
23
|
+
command.new.call(*args)
|
24
|
+
else
|
25
|
+
raise(
|
26
|
+
Karafka::Errors::UnrecognizedCommandError,
|
27
|
+
"Unrecognized command \"#{command_name}\""
|
28
|
+
)
|
21
29
|
end
|
22
30
|
end
|
23
31
|
|
24
32
|
private
|
25
33
|
|
26
|
-
# @return [Array<Class>]
|
27
|
-
def
|
28
|
-
|
29
|
-
.map! { |object| const_get(object) }
|
30
|
-
.keep_if do |object|
|
31
|
-
object.instance_of?(Class) && (object < Cli::Base)
|
32
|
-
end
|
34
|
+
# @return [Array<Class>] command classes
|
35
|
+
def commands
|
36
|
+
Base.commands
|
33
37
|
end
|
34
38
|
end
|
35
39
|
end
|
@@ -43,7 +43,11 @@ module Karafka
|
|
43
43
|
@closed = false
|
44
44
|
@subscription_group = subscription_group
|
45
45
|
@buffer = RawMessagesBuffer.new
|
46
|
-
@rebalance_manager = RebalanceManager.new
|
46
|
+
@rebalance_manager = RebalanceManager.new(@subscription_group.id)
|
47
|
+
@rebalance_callback = Instrumentation::Callbacks::Rebalance.new(
|
48
|
+
@subscription_group.id,
|
49
|
+
@subscription_group.consumer_group.id
|
50
|
+
)
|
47
51
|
@kafka = build_consumer
|
48
52
|
# There are few operations that can happen in parallel from the listener threads as well
|
49
53
|
# as from the workers. They are not fully thread-safe because they may be composed out of
|
@@ -498,7 +502,8 @@ module Karafka
|
|
498
502
|
def build_consumer
|
499
503
|
::Rdkafka::Config.logger = ::Karafka::App.config.logger
|
500
504
|
config = ::Rdkafka::Config.new(@subscription_group.kafka)
|
501
|
-
config.consumer_rebalance_listener = @
|
505
|
+
config.consumer_rebalance_listener = @rebalance_callback
|
506
|
+
|
502
507
|
consumer = config.consumer
|
503
508
|
@name = consumer.name
|
504
509
|
|
@@ -507,7 +512,7 @@ module Karafka
|
|
507
512
|
@subscription_group.id,
|
508
513
|
Instrumentation::Callbacks::Statistics.new(
|
509
514
|
@subscription_group.id,
|
510
|
-
@subscription_group.
|
515
|
+
@subscription_group.consumer_group.id,
|
511
516
|
@name
|
512
517
|
)
|
513
518
|
)
|
@@ -517,7 +522,7 @@ module Karafka
|
|
517
522
|
@subscription_group.id,
|
518
523
|
Instrumentation::Callbacks::Error.new(
|
519
524
|
@subscription_group.id,
|
520
|
-
@subscription_group.
|
525
|
+
@subscription_group.consumer_group.id,
|
521
526
|
@name
|
522
527
|
)
|
523
528
|
)
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Karafka
|
4
4
|
module Connection
|
5
|
-
# Manager for tracking changes in the partitions assignment.
|
5
|
+
# Manager for tracking changes in the partitions assignment after the assignment is done.
|
6
6
|
#
|
7
7
|
# We need tracking of those to clean up consumers that will no longer process given partitions
|
8
8
|
# as they were taken away.
|
@@ -17,6 +17,10 @@ module Karafka
|
|
17
17
|
#
|
18
18
|
# @note For cooperative-sticky `#assigned_partitions` holds only the recently assigned
|
19
19
|
# partitions, not all the partitions that are owned
|
20
|
+
#
|
21
|
+
# @note We have to have the `subscription_group` reference because we have a global pipeline
|
22
|
+
# for notifications and we need to make sure we track changes only for things that are of
|
23
|
+
# relevance to our subscription group
|
20
24
|
class RebalanceManager
|
21
25
|
# Empty array for internal usage not to create new objects
|
22
26
|
EMPTY_ARRAY = [].freeze
|
@@ -25,12 +29,17 @@ module Karafka
|
|
25
29
|
|
26
30
|
private_constant :EMPTY_ARRAY
|
27
31
|
|
32
|
+
# @param subscription_group_id [String] subscription group id
|
28
33
|
# @return [RebalanceManager]
|
29
|
-
def initialize
|
34
|
+
def initialize(subscription_group_id)
|
30
35
|
@assigned_partitions = {}
|
31
36
|
@revoked_partitions = {}
|
32
37
|
@changed = false
|
33
38
|
@active = false
|
39
|
+
@subscription_group_id = subscription_group_id
|
40
|
+
|
41
|
+
# Connects itself to the instrumentation pipeline so rebalances can be tracked
|
42
|
+
::Karafka.monitor.subscribe(self)
|
34
43
|
end
|
35
44
|
|
36
45
|
# Resets the rebalance manager state
|
@@ -55,36 +64,42 @@ module Karafka
|
|
55
64
|
@active
|
56
65
|
end
|
57
66
|
|
58
|
-
#
|
67
|
+
# We consider as lost only partitions that were taken away and not re-assigned back to us
|
68
|
+
def lost_partitions
|
69
|
+
lost_partitions = {}
|
70
|
+
|
71
|
+
revoked_partitions.each do |topic, partitions|
|
72
|
+
lost_partitions[topic] = partitions - assigned_partitions.fetch(topic, EMPTY_ARRAY)
|
73
|
+
end
|
74
|
+
|
75
|
+
lost_partitions
|
76
|
+
end
|
77
|
+
|
78
|
+
# Callback that kicks in inside of rdkafka, when new partitions were assigned.
|
59
79
|
#
|
60
80
|
# @private
|
61
|
-
# @param
|
62
|
-
def
|
81
|
+
# @param event [Karafka::Core::Monitoring::Event]
|
82
|
+
def on_rebalance_partitions_assigned(event)
|
83
|
+
# Apply changes only for our subscription group
|
84
|
+
return unless event[:subscription_group_id] == @subscription_group_id
|
85
|
+
|
63
86
|
@active = true
|
64
|
-
@assigned_partitions =
|
87
|
+
@assigned_partitions = event[:tpl].to_h.transform_values { |part| part.map(&:partition) }
|
65
88
|
@changed = true
|
66
89
|
end
|
67
90
|
|
68
|
-
# Callback that kicks in inside of rdkafka, when partitions
|
91
|
+
# Callback that kicks in inside of rdkafka, when partitions were revoked.
|
69
92
|
#
|
70
93
|
# @private
|
71
|
-
# @param
|
72
|
-
def
|
94
|
+
# @param event [Karafka::Core::Monitoring::Event]
|
95
|
+
def on_rebalance_partitions_revoked(event)
|
96
|
+
# Apply changes only for our subscription group
|
97
|
+
return unless event[:subscription_group_id] == @subscription_group_id
|
98
|
+
|
73
99
|
@active = true
|
74
|
-
@revoked_partitions =
|
100
|
+
@revoked_partitions = event[:tpl].to_h.transform_values { |part| part.map(&:partition) }
|
75
101
|
@changed = true
|
76
102
|
end
|
77
|
-
|
78
|
-
# We consider as lost only partitions that were taken away and not re-assigned back to us
|
79
|
-
def lost_partitions
|
80
|
-
lost_partitions = {}
|
81
|
-
|
82
|
-
revoked_partitions.each do |topic, partitions|
|
83
|
-
lost_partitions[topic] = partitions - assigned_partitions.fetch(topic, EMPTY_ARRAY)
|
84
|
-
end
|
85
|
-
|
86
|
-
lost_partitions
|
87
|
-
end
|
88
103
|
end
|
89
104
|
end
|
90
105
|
end
|