karafka 1.3.0 → 1.4.14
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.diffend.yml +3 -0
- data/.github/workflows/ci.yml +76 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +112 -15
- data/CODE_OF_CONDUCT.md +1 -1
- data/Gemfile +2 -0
- data/Gemfile.lock +87 -98
- data/README.md +28 -31
- data/certs/mensfeld.pem +24 -23
- data/config/errors.yml +2 -0
- data/docker-compose.yml +17 -0
- data/karafka.gemspec +22 -14
- data/lib/karafka/assignment_strategies/round_robin.rb +13 -0
- data/lib/karafka/attributes_map.rb +3 -8
- data/lib/karafka/cli/base.rb +4 -4
- data/lib/karafka/cli/flow.rb +9 -6
- data/lib/karafka/cli/info.rb +1 -1
- data/lib/karafka/cli/install.rb +5 -2
- data/lib/karafka/cli/missingno.rb +19 -0
- data/lib/karafka/cli/server.rb +8 -8
- data/lib/karafka/cli.rb +9 -1
- data/lib/karafka/connection/api_adapter.rb +27 -24
- data/lib/karafka/connection/batch_delegator.rb +5 -1
- data/lib/karafka/connection/builder.rb +9 -2
- data/lib/karafka/connection/client.rb +9 -6
- data/lib/karafka/connection/listener.rb +2 -2
- data/lib/karafka/consumers/batch_metadata.rb +10 -0
- data/lib/karafka/consumers/includer.rb +5 -4
- data/lib/karafka/contracts/consumer_group.rb +10 -5
- data/lib/karafka/contracts/server_cli_options.rb +2 -0
- data/lib/karafka/contracts.rb +1 -1
- data/lib/karafka/helpers/class_matcher.rb +2 -2
- data/lib/karafka/instrumentation/logger.rb +6 -9
- data/lib/karafka/instrumentation/stdout_listener.rb +6 -4
- data/lib/karafka/params/batch_metadata.rb +26 -0
- data/lib/karafka/params/builders/batch_metadata.rb +30 -0
- data/lib/karafka/params/builders/params.rb +17 -15
- data/lib/karafka/params/builders/params_batch.rb +2 -2
- data/lib/karafka/params/metadata.rb +14 -29
- data/lib/karafka/params/params.rb +27 -41
- data/lib/karafka/params/params_batch.rb +15 -16
- data/lib/karafka/routing/builder.rb +1 -0
- data/lib/karafka/routing/consumer_group.rb +5 -3
- data/lib/karafka/serialization/json/deserializer.rb +2 -2
- data/lib/karafka/server.rb +4 -1
- data/lib/karafka/setup/config.rb +60 -52
- data/lib/karafka/templates/karafka.rb.erb +1 -1
- data/lib/karafka/version.rb +1 -1
- data/lib/karafka.rb +3 -1
- data.tar.gz.sig +0 -0
- metadata +75 -93
- metadata.gz.sig +0 -0
- data/.github/FUNDING.yml +0 -3
- data/.travis.yml +0 -36
- data/lib/karafka/consumers/metadata.rb +0 -10
- data/lib/karafka/params/builders/metadata.rb +0 -33
data/certs/mensfeld.pem
CHANGED
@@ -1,25 +1,26 @@
|
|
1
1
|
-----BEGIN CERTIFICATE-----
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
2
|
+
MIIEcDCCAtigAwIBAgIBATANBgkqhkiG9w0BAQsFADA/MRAwDgYDVQQDDAdjb250
|
3
|
+
YWN0MRcwFQYKCZImiZPyLGQBGRYHa2FyYWZrYTESMBAGCgmSJomT8ixkARkWAmlv
|
4
|
+
MB4XDTIyMDgxOTE3MjEzN1oXDTIzMDgxOTE3MjEzN1owPzEQMA4GA1UEAwwHY29u
|
5
|
+
dGFjdDEXMBUGCgmSJomT8ixkARkWB2thcmFma2ExEjAQBgoJkiaJk/IsZAEZFgJp
|
6
|
+
bzCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAODzeO3L6lxdATzMHKNW
|
7
|
+
jFA/GGunoPuylO/BMzy8RiQHh7VIvysAKs0tHhTx3g2D0STDpF+hcQcPELFikiT2
|
8
|
+
F+1wOHj/SsrK7VKqfA8+gq04hKc5sQoX2Egf9k3V0YJ3eZ6R/koHkQ8A0TVt0w6F
|
9
|
+
ZQckoV4MqnEAx0g/FZN3mnHTlJ3VFLSBqJEIe+S6FZMl92mSv+hTrlUG8VaYxSfN
|
10
|
+
lTCvnKk284F6QZq5XIENLRmcDd/3aPBLnLwNnyMyhB+6gK8cUO+CFlDO5tjo/aBA
|
11
|
+
rUnl++wGG0JooF1ed0v+evOn9KoMBG6rHewcf79qJbVOscbD8qSAmo+sCXtcFryr
|
12
|
+
KRMTB8gNbowJkFRJDEe8tfRy11u1fYzFg/qNO82FJd62rKAw2wN0C29yCeQOPRb1
|
13
|
+
Cw9Y4ZwK9VFNEcV9L+3pHTHn2XfuZHtDaG198VweiF6raFO4yiEYccodH/USP0L5
|
14
|
+
cbcCFtmu/4HDSxL1ByQXO84A0ybJuk3/+aPUSXe9C9U8fwIDAQABo3cwdTAJBgNV
|
15
|
+
HRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUSlcEakb7gfn/5E2WY6z73BF/
|
16
|
+
iZkwHQYDVR0RBBYwFIESY29udGFjdEBrYXJhZmthLmlvMB0GA1UdEgQWMBSBEmNv
|
17
|
+
bnRhY3RAa2FyYWZrYS5pbzANBgkqhkiG9w0BAQsFAAOCAYEA1aS+E7RXJ1w9g9mJ
|
18
|
+
G0NzFxe64OEuENosNlvYQCbRKGCXAU1qqelYkBQHseRgRKxLICrnypRo9IEobyHa
|
19
|
+
vDnJ4r7Tsb34dleqQW2zY/obG+cia3Ym2JsegXWF7dDOzCXJ4FN8MFoT2jHlqLLw
|
20
|
+
yrap0YO5zx0GSQ0Dwy8h2n2v2vanMEeCx7iNm3ERgR5WuN5sjzWoz2A/JLEEcK0C
|
21
|
+
EnAGKCWAd1fuG8IemDjT1edsd5FyYR4bIX0m+99oDuFZyPiiIbalmyYiSBBp59Yb
|
22
|
+
Q0P8zeBi4OfwCZNcxqz0KONmw9JLNv6DgyEAH5xe/4JzhMEgvIRiPj0pHfA7oqQF
|
23
|
+
KUNqvD1KlxbEC+bZfE5IZhnqYLdld/Ksqd22FI1RBhiS1Ejfsj99LVIm9cBuZEY2
|
24
|
+
Qf04B9ceLUaC4fPVEz10FyobjaFoY4i32xRto3XnrzeAgfEe4swLq8bQsR3w/EF3
|
25
|
+
MGU0FeSV2Yj7Xc2x/7BzLK8xQn5l7Yy75iPF+KP3vVmDHnNl
|
25
26
|
-----END CERTIFICATE-----
|
data/config/errors.yml
CHANGED
data/docker-compose.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
version: '2'
|
2
|
+
services:
|
3
|
+
zookeeper:
|
4
|
+
image: wurstmeister/zookeeper
|
5
|
+
ports:
|
6
|
+
- "2181:2181"
|
7
|
+
kafka:
|
8
|
+
image: wurstmeister/kafka:1.0.1
|
9
|
+
ports:
|
10
|
+
- "9092:9092"
|
11
|
+
environment:
|
12
|
+
KAFKA_ADVERTISED_HOST_NAME: localhost
|
13
|
+
KAFKA_ADVERTISED_PORT: 9092
|
14
|
+
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
|
15
|
+
KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
|
16
|
+
volumes:
|
17
|
+
- /var/run/docker.sock:/var/run/docker.sock
|
data/karafka.gemspec
CHANGED
@@ -11,26 +11,24 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.version = ::Karafka::VERSION
|
12
12
|
spec.platform = Gem::Platform::RUBY
|
13
13
|
spec.authors = ['Maciej Mensfeld', 'Pavlo Vavruk', 'Adam Gwozdowski']
|
14
|
-
spec.email = %w[maciej@
|
15
|
-
spec.homepage = 'https://
|
14
|
+
spec.email = %w[maciej@mensfeld.pl pavlo.vavruk@gmail.com adam99g@gmail.com]
|
15
|
+
spec.homepage = 'https://karafka.io'
|
16
16
|
spec.summary = 'Ruby based framework for working with Apache Kafka'
|
17
17
|
spec.description = 'Framework used to simplify Apache Kafka based Ruby applications development'
|
18
18
|
spec.license = 'MIT'
|
19
19
|
|
20
|
-
spec.add_dependency '
|
21
|
-
spec.add_dependency 'dry-
|
22
|
-
spec.add_dependency 'dry-
|
23
|
-
spec.add_dependency 'dry-
|
20
|
+
spec.add_dependency 'concurrent-ruby'
|
21
|
+
spec.add_dependency 'dry-configurable', '~> 0.16'
|
22
|
+
spec.add_dependency 'dry-inflector', '~> 0.2'
|
23
|
+
spec.add_dependency 'dry-monitor', '~> 0.5'
|
24
|
+
spec.add_dependency 'dry-validation', '~> 1.7'
|
24
25
|
spec.add_dependency 'envlogic', '~> 1.1'
|
25
|
-
spec.add_dependency '
|
26
|
-
spec.add_dependency '
|
27
|
-
spec.add_dependency '
|
28
|
-
spec.add_dependency '
|
29
|
-
spec.add_dependency 'thor', '~> 0.20'
|
30
|
-
spec.add_dependency 'waterdrop', '~> 1.3.0'
|
31
|
-
spec.add_dependency 'zeitwerk', '~> 2.1'
|
26
|
+
spec.add_dependency 'ruby-kafka', '>= 1.3.0'
|
27
|
+
spec.add_dependency 'thor', '>= 1.1'
|
28
|
+
spec.add_dependency 'waterdrop', '~> 1.4'
|
29
|
+
spec.add_dependency 'zeitwerk', '~> 2.6'
|
32
30
|
|
33
|
-
spec.required_ruby_version = '>= 2.
|
31
|
+
spec.required_ruby_version = '>= 2.7'
|
34
32
|
|
35
33
|
if $PROGRAM_NAME.end_with?('gem')
|
36
34
|
spec.signing_key = File.expand_path('~/.ssh/gem-private_key.pem')
|
@@ -40,5 +38,15 @@ Gem::Specification.new do |spec|
|
|
40
38
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
|
41
39
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
42
40
|
spec.require_paths = %w[lib]
|
41
|
+
spec.post_install_message = <<~MSG
|
42
|
+
WARN: Karafka 1.4 will reach the end of life soon.
|
43
|
+
We highly recommend updating to Karafka 2.0.
|
44
|
+
Visit this page for more details: https://karafka.io/docs/Versions-Lifecycle-and-EOL
|
45
|
+
MSG
|
46
|
+
|
47
|
+
spec.metadata = {
|
48
|
+
'source_code_uri' => 'https://github.com/karafka/karafka',
|
49
|
+
'rubygems_mfa_required' => 'true'
|
50
|
+
}
|
43
51
|
end
|
44
52
|
# rubocop:enable Metrics/BlockLength
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
# Strategies for Kafka partitions assignments
|
5
|
+
module AssignmentStrategies
|
6
|
+
# Standard RoundRobin strategy
|
7
|
+
class RoundRobin < SimpleDelegator
|
8
|
+
def initialize
|
9
|
+
super(Kafka::RoundRobinAssignmentStrategy.new)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -19,6 +19,7 @@ module Karafka
|
|
19
19
|
consumer: %i[
|
20
20
|
session_timeout offset_commit_interval offset_commit_threshold
|
21
21
|
offset_retention_time heartbeat_interval fetcher_max_queue_size
|
22
|
+
assignment_strategy
|
22
23
|
],
|
23
24
|
subscribe: %i[start_from_beginning max_bytes_per_partition],
|
24
25
|
consumption: %i[min_bytes max_bytes max_wait_time],
|
@@ -52,14 +53,8 @@ module Karafka
|
|
52
53
|
ignored_settings = api_adapter[:subscribe]
|
53
54
|
defined_settings = api_adapter.values.flatten
|
54
55
|
karafka_settings = %i[batch_fetching]
|
55
|
-
|
56
|
-
dynamically_proxied = Karafka::Setup::Config
|
57
|
-
._settings
|
58
|
-
.settings
|
59
|
-
.find { |s| s.name == :kafka }
|
60
|
-
.value
|
61
|
-
.names
|
62
|
-
.to_a
|
56
|
+
|
57
|
+
dynamically_proxied = Karafka::Setup::Config.config.kafka.to_h.keys
|
63
58
|
|
64
59
|
(defined_settings + dynamically_proxied).uniq + karafka_settings - ignored_settings
|
65
60
|
end
|
data/lib/karafka/cli/base.rb
CHANGED
@@ -43,16 +43,16 @@ module Karafka
|
|
43
43
|
end
|
44
44
|
|
45
45
|
# Allows to set description of a given cli command
|
46
|
-
# @param
|
47
|
-
def desc(
|
48
|
-
@desc ||=
|
46
|
+
# @param args [Array] All the arguments that Thor desc method accepts
|
47
|
+
def desc(*args)
|
48
|
+
@desc ||= args
|
49
49
|
end
|
50
50
|
|
51
51
|
# This method will bind a given Cli command into Karafka Cli
|
52
52
|
# This method is a wrapper to way Thor defines its commands
|
53
53
|
# @param cli_class [Karafka::Cli] Karafka cli_class
|
54
54
|
def bind_to(cli_class)
|
55
|
-
cli_class.desc name,
|
55
|
+
cli_class.desc name, *@desc
|
56
56
|
|
57
57
|
(@options || []).each { |option| cli_class.option(*option) }
|
58
58
|
|
data/lib/karafka/cli/flow.rb
CHANGED
@@ -11,19 +11,22 @@ module Karafka
|
|
11
11
|
def call
|
12
12
|
topics.each do |topic|
|
13
13
|
any_topics = !topic.responder&.topics.nil?
|
14
|
+
log_messages = []
|
14
15
|
|
15
16
|
if any_topics
|
16
|
-
|
17
|
+
log_messages << "#{topic.name} =>"
|
17
18
|
|
18
19
|
topic.responder.topics.each_value do |responder_topic|
|
19
20
|
features = []
|
20
21
|
features << (responder_topic.required? ? 'always' : 'conditionally')
|
21
22
|
|
22
|
-
|
23
|
+
log_messages << format(responder_topic.name, "(#{features.join(', ')})")
|
23
24
|
end
|
24
25
|
else
|
25
|
-
|
26
|
+
log_messages << "#{topic.name} => (nothing)"
|
26
27
|
end
|
28
|
+
|
29
|
+
Karafka.logger.info(log_messages.join("\n"))
|
27
30
|
end
|
28
31
|
end
|
29
32
|
|
@@ -34,11 +37,11 @@ module Karafka
|
|
34
37
|
Karafka::App.consumer_groups.map(&:topics).flatten.sort_by(&:name)
|
35
38
|
end
|
36
39
|
|
37
|
-
#
|
40
|
+
# Formats a given value with label in a nice way
|
38
41
|
# @param label [String] label describing value
|
39
42
|
# @param value [String] value that should be printed
|
40
|
-
def
|
41
|
-
|
43
|
+
def format(label, value)
|
44
|
+
" - #{label}: #{value}"
|
42
45
|
end
|
43
46
|
end
|
44
47
|
end
|
data/lib/karafka/cli/info.rb
CHANGED
data/lib/karafka/cli/install.rb
CHANGED
@@ -13,7 +13,9 @@ module Karafka
|
|
13
13
|
INSTALL_DIRS = %w[
|
14
14
|
app/consumers
|
15
15
|
app/responders
|
16
|
+
app/workers
|
16
17
|
config
|
18
|
+
lib
|
17
19
|
log
|
18
20
|
tmp/pids
|
19
21
|
].freeze
|
@@ -28,11 +30,12 @@ module Karafka
|
|
28
30
|
# @param args [Array] all the things that Thor CLI accepts
|
29
31
|
def initialize(*args)
|
30
32
|
super
|
31
|
-
|
33
|
+
dependencies = Bundler::LockfileParser.new(
|
32
34
|
Bundler.read_file(
|
33
35
|
Bundler.default_lockfile
|
34
36
|
)
|
35
|
-
).dependencies
|
37
|
+
).dependencies
|
38
|
+
@rails = dependencies.key?('railties') || dependencies.key?('rails')
|
36
39
|
end
|
37
40
|
|
38
41
|
# Install all required things for Karafka application in current directory
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
class Cli < Thor
|
5
|
+
# Command that gets invoked when no method is provided when running the CLI
|
6
|
+
# It allows us to exit with exit code 1 instead of default 0 to indicate that something
|
7
|
+
# was missing
|
8
|
+
# @see https://github.com/karafka/karafka/issues/619
|
9
|
+
class Missingno < Base
|
10
|
+
desc 'Hidden command that gets invoked when no command is provided', hide: true
|
11
|
+
|
12
|
+
# Prints an error about the lack of command (nothing selected)
|
13
|
+
def call
|
14
|
+
Karafka.logger.error('No command provided')
|
15
|
+
exit 1
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/karafka/cli/server.rb
CHANGED
@@ -31,14 +31,6 @@ module Karafka
|
|
31
31
|
# part of the topics
|
32
32
|
Karafka::Server.consumer_groups = cli.options[:consumer_groups]
|
33
33
|
|
34
|
-
# Remove pidfile on stop, just before the server instance is going to be GCed
|
35
|
-
# We want to delay the moment in which the pidfile is removed as much as we can,
|
36
|
-
# so instead of removing it after the server stops running, we rely on the gc moment
|
37
|
-
# when this object gets removed (it is a bit later), so it is closer to the actual
|
38
|
-
# system process end. We do that, so monitoring and deployment tools that rely on a pid
|
39
|
-
# won't alarm or start new system process up until the current one is finished
|
40
|
-
ObjectSpace.define_finalizer(self, proc { send(:clean) })
|
41
|
-
|
42
34
|
Karafka::Server.run
|
43
35
|
end
|
44
36
|
|
@@ -60,6 +52,14 @@ module Karafka
|
|
60
52
|
cli.options[:pid],
|
61
53
|
'w'
|
62
54
|
) { |file| file.write(::Process.pid) }
|
55
|
+
|
56
|
+
# Remove pidfile on stop, just before the server instance is going to be GCed
|
57
|
+
# We want to delay the moment in which the pidfile is removed as much as we can,
|
58
|
+
# so instead of removing it after the server stops running, we rely on the gc moment
|
59
|
+
# when this object gets removed (it is a bit later), so it is closer to the actual
|
60
|
+
# system process end. We do that, so monitoring and deployment tools that rely on a pid
|
61
|
+
# won't alarm or start new system process up until the current one is finished
|
62
|
+
ObjectSpace.define_finalizer(self, proc { send(:clean) })
|
63
63
|
end
|
64
64
|
|
65
65
|
# Removes a pidfile (if exist)
|
data/lib/karafka/cli.rb
CHANGED
@@ -10,6 +10,8 @@ module Karafka
|
|
10
10
|
class Cli < Thor
|
11
11
|
package_name 'Karafka'
|
12
12
|
|
13
|
+
default_task :missingno
|
14
|
+
|
13
15
|
class << self
|
14
16
|
# Loads all Cli commands into Thor framework
|
15
17
|
# This method should be executed before we run Karafka::Cli.start, otherwise we won't
|
@@ -20,6 +22,12 @@ module Karafka
|
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
25
|
+
# When there is a CLI crash, exit
|
26
|
+
# @return [true]
|
27
|
+
def exit_on_failure?
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
23
31
|
private
|
24
32
|
|
25
33
|
# @return [Array<Class>] Array with Cli action classes that can be used as commands
|
@@ -47,7 +55,7 @@ end
|
|
47
55
|
if ENV['KARAFKA_CONSOLE']
|
48
56
|
# Reloads Karafka irb console session
|
49
57
|
def reload!
|
50
|
-
|
58
|
+
Karafka.logger.info "Reloading...\n"
|
51
59
|
Kernel.exec Karafka::Cli::Console.command
|
52
60
|
end
|
53
61
|
end
|
@@ -14,11 +14,12 @@ module Karafka
|
|
14
14
|
module ApiAdapter
|
15
15
|
class << self
|
16
16
|
# Builds all the configuration settings for Kafka.new method
|
17
|
+
# @param consumer_group [Karafka::Routing::ConsumerGroup] consumer group details
|
17
18
|
# @return [Array<Hash>] Array with all the client arguments including hash with all
|
18
19
|
# the settings required by Kafka.new method
|
19
20
|
# @note We return array, so we can inject any arguments we want, in case of changes in the
|
20
21
|
# raw driver
|
21
|
-
def client
|
22
|
+
def client(consumer_group)
|
22
23
|
# This one is a default that takes all the settings except special
|
23
24
|
# cases defined in the map
|
24
25
|
settings = {
|
@@ -26,14 +27,17 @@ module Karafka
|
|
26
27
|
client_id: ::Karafka::App.config.client_id
|
27
28
|
}
|
28
29
|
|
29
|
-
kafka_configs.
|
30
|
+
kafka_configs.each_key do |setting_name|
|
30
31
|
# All options for config adapter should be ignored as we're just interested
|
31
32
|
# in what is left, as we want to pass all the options that are "typical"
|
32
33
|
# and not listed in the api_adapter special cases mapping. All the values
|
33
34
|
# from the api_adapter mapping go somewhere else, not to the client directly
|
34
35
|
next if AttributesMap.api_adapter.values.flatten.include?(setting_name)
|
35
36
|
|
36
|
-
|
37
|
+
# Settings for each consumer group are either defined per consumer group or are
|
38
|
+
# inherited from the global/general settings level, thus we don't have to fetch them
|
39
|
+
# from the kafka settings as they are already on a consumer group level
|
40
|
+
settings[setting_name] = consumer_group.public_send(setting_name)
|
37
41
|
end
|
38
42
|
|
39
43
|
settings_hash = sanitize(settings)
|
@@ -44,30 +48,28 @@ module Karafka
|
|
44
48
|
|
45
49
|
# Builds all the configuration settings for kafka#consumer method
|
46
50
|
# @param consumer_group [Karafka::Routing::ConsumerGroup] consumer group details
|
47
|
-
# @return [
|
51
|
+
# @return [Hash] all the consumer keyword arguments including hash with all
|
48
52
|
# the settings required by Kafka#consumer
|
49
53
|
def consumer(consumer_group)
|
50
54
|
settings = { group_id: consumer_group.id }
|
51
55
|
settings = fetch_for(:consumer, consumer_group, settings)
|
52
|
-
|
56
|
+
sanitize(settings)
|
53
57
|
end
|
54
58
|
|
55
59
|
# Builds all the configuration settings for kafka consumer consume_each_batch and
|
56
60
|
# consume_each_message methods
|
57
61
|
# @param consumer_group [Karafka::Routing::ConsumerGroup] consumer group details
|
58
|
-
# @return [
|
59
|
-
# including
|
62
|
+
# @return [Hash] hash with all the arguments required by consuming method
|
63
|
+
# including all the settings required by
|
60
64
|
# Kafka::Consumer#consume_each_message and Kafka::Consumer#consume_each_batch method
|
61
65
|
def consumption(consumer_group)
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
automatically_mark_as_processed: consumer_group.automatically_mark_as_consumed
|
68
|
-
)
|
66
|
+
sanitize(
|
67
|
+
fetch_for(
|
68
|
+
:consumption,
|
69
|
+
consumer_group,
|
70
|
+
automatically_mark_as_processed: consumer_group.automatically_mark_as_consumed
|
69
71
|
)
|
70
|
-
|
72
|
+
)
|
71
73
|
end
|
72
74
|
|
73
75
|
# Builds all the configuration settings for kafka consumer#subscribe method
|
@@ -82,17 +84,16 @@ module Karafka
|
|
82
84
|
# @param topic [String] topic that we want to pause
|
83
85
|
# @param partition [Integer] number partition that we want to pause
|
84
86
|
# @param consumer_group [Karafka::Routing::ConsumerGroup] consumer group details
|
85
|
-
# @return [
|
87
|
+
# @return [Hash] hash with all the details required to pause kafka consumer
|
86
88
|
def pause(topic, partition, consumer_group)
|
87
|
-
|
88
|
-
Karafka::App.config.topic_mapper.outgoing(topic),
|
89
|
-
|
90
|
-
{
|
89
|
+
{
|
90
|
+
args: [Karafka::App.config.topic_mapper.outgoing(topic), partition],
|
91
|
+
kwargs: {
|
91
92
|
timeout: consumer_group.pause_timeout,
|
92
93
|
max_timeout: consumer_group.pause_max_timeout,
|
93
94
|
exponential_backoff: consumer_group.pause_exponential_backoff
|
94
95
|
}
|
95
|
-
|
96
|
+
}
|
96
97
|
end
|
97
98
|
|
98
99
|
# Remaps topic details taking the topic mapper feature into consideration.
|
@@ -105,11 +106,13 @@ module Karafka
|
|
105
106
|
# Majority of users don't use custom topic mappers. No need to change anything when it
|
106
107
|
# is a default mapper that does not change anything. Only some cloud providers require
|
107
108
|
# topics to be remapped
|
108
|
-
return [params] if Karafka::App.config.topic_mapper.is_a?(
|
109
|
+
return [params.metadata] if Karafka::App.config.topic_mapper.is_a?(
|
110
|
+
Karafka::Routing::TopicMapper
|
111
|
+
)
|
109
112
|
|
110
113
|
# @note We don't use tap as it is around 13% slower than non-dup version
|
111
|
-
dupped = params.dup
|
112
|
-
dupped['topic'] = Karafka::App.config.topic_mapper.outgoing(params.topic)
|
114
|
+
dupped = params.metadata.dup
|
115
|
+
dupped['topic'] = Karafka::App.config.topic_mapper.outgoing(params.metadata.topic)
|
113
116
|
[dupped]
|
114
117
|
end
|
115
118
|
|
@@ -23,7 +23,11 @@ module Karafka
|
|
23
23
|
) do
|
24
24
|
# Due to how ruby-kafka is built, we have the metadata that is stored on the batch
|
25
25
|
# level only available for batch consuming
|
26
|
-
consumer.
|
26
|
+
consumer.batch_metadata = Params::Builders::BatchMetadata.from_kafka_batch(
|
27
|
+
kafka_batch,
|
28
|
+
topic
|
29
|
+
)
|
30
|
+
|
27
31
|
kafka_messages = kafka_batch.messages
|
28
32
|
|
29
33
|
# Depending on a case (persisted or not) we might use new consumer instance per
|
@@ -6,9 +6,16 @@ module Karafka
|
|
6
6
|
module Builder
|
7
7
|
class << self
|
8
8
|
# Builds a Kafka::Client instance that we use to work with Kafka cluster
|
9
|
+
# @param consumer_group [Karafka::Routing::ConsumerGroup] consumer group for which we want
|
10
|
+
# to have a new Kafka client
|
9
11
|
# @return [::Kafka::Client] returns a Kafka client
|
10
|
-
def call
|
11
|
-
|
12
|
+
def call(consumer_group)
|
13
|
+
settings = ApiAdapter.client(consumer_group)
|
14
|
+
|
15
|
+
Kafka.new(
|
16
|
+
settings[0],
|
17
|
+
**settings[1]
|
18
|
+
)
|
12
19
|
end
|
13
20
|
end
|
14
21
|
end
|
@@ -33,9 +33,9 @@ module Karafka
|
|
33
33
|
settings = ApiAdapter.consumption(consumer_group)
|
34
34
|
|
35
35
|
if consumer_group.batch_fetching
|
36
|
-
kafka_consumer.each_batch(
|
36
|
+
kafka_consumer.each_batch(**settings) { |batch| yield(batch, :batch) }
|
37
37
|
else
|
38
|
-
kafka_consumer.each_message(
|
38
|
+
kafka_consumer.each_message(**settings) { |message| yield(message, :message) }
|
39
39
|
end
|
40
40
|
# @note We catch only the processing errors as any other are considered critical (exceptions)
|
41
41
|
# and should require a client restart with a backoff
|
@@ -64,7 +64,8 @@ module Karafka
|
|
64
64
|
# @param topic [String] topic that we want to pause
|
65
65
|
# @param partition [Integer] number partition that we want to pause
|
66
66
|
def pause(topic, partition)
|
67
|
-
|
67
|
+
args, kwargs = ApiAdapter.pause(topic, partition, consumer_group).values_at(:args, :kwargs)
|
68
|
+
kafka_consumer.pause(*args, **kwargs)
|
68
69
|
end
|
69
70
|
|
70
71
|
# Marks given message as consumed
|
@@ -97,11 +98,13 @@ module Karafka
|
|
97
98
|
def kafka_consumer
|
98
99
|
# @note We don't cache the connection internally because we cache kafka_consumer that uses
|
99
100
|
# kafka client object instance
|
100
|
-
@kafka_consumer ||= Builder.call.consumer(
|
101
|
-
|
101
|
+
@kafka_consumer ||= Builder.call(consumer_group).consumer(
|
102
|
+
**ApiAdapter.consumer(consumer_group)
|
102
103
|
).tap do |consumer|
|
103
104
|
consumer_group.topics.each do |topic|
|
104
|
-
|
105
|
+
settings = ApiAdapter.subscribe(topic)
|
106
|
+
|
107
|
+
consumer.subscribe(settings[0], **settings[1])
|
105
108
|
end
|
106
109
|
end
|
107
110
|
rescue Kafka::ConnectionError
|
@@ -47,10 +47,10 @@ module Karafka
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
# This is on purpose - see the notes for this method
|
50
|
-
# rubocop:disable RescueException
|
50
|
+
# rubocop:disable Lint/RescueException
|
51
51
|
rescue Exception => e
|
52
52
|
Karafka.monitor.instrument('connection.listener.fetch_loop.error', caller: self, error: e)
|
53
|
-
# rubocop:enable RescueException
|
53
|
+
# rubocop:enable Lint/RescueException
|
54
54
|
# We can stop client without a problem, as it will reinitialize itself when running the
|
55
55
|
# `fetch_loop` again
|
56
56
|
@client.stop
|
@@ -16,7 +16,7 @@ module Karafka
|
|
16
16
|
|
17
17
|
bind_backend(consumer, topic)
|
18
18
|
bind_params(consumer, topic)
|
19
|
-
|
19
|
+
bind_batch_metadata(consumer, topic)
|
20
20
|
bind_responders(consumer, topic)
|
21
21
|
end
|
22
22
|
|
@@ -40,13 +40,14 @@ module Karafka
|
|
40
40
|
consumer.extend(SingleParams)
|
41
41
|
end
|
42
42
|
|
43
|
-
# Adds an option to work with metadata for consumer instances that have
|
43
|
+
# Adds an option to work with batch metadata for consumer instances that have
|
44
|
+
# batch fetching enabled
|
44
45
|
# @param consumer [Karafka::BaseConsumer] consumer instance
|
45
46
|
# @param topic [Karafka::Routing::Topic] topic of a consumer class
|
46
|
-
def
|
47
|
+
def bind_batch_metadata(consumer, topic)
|
47
48
|
return unless topic.batch_fetching
|
48
49
|
|
49
|
-
consumer.extend(
|
50
|
+
consumer.extend(BatchMetadata)
|
50
51
|
end
|
51
52
|
|
52
53
|
# Adds responders support for topics and consumers with responders defined for them
|
@@ -32,6 +32,7 @@ module Karafka
|
|
32
32
|
required(:offset_retention_time).maybe(:integer)
|
33
33
|
required(:heartbeat_interval).filled { (int? | float?) & gteq?(0) }
|
34
34
|
required(:fetcher_max_queue_size).filled(:int?, gt?: 0)
|
35
|
+
required(:assignment_strategy).value(:any)
|
35
36
|
required(:connect_timeout).filled { (int? | float?) & gt?(0) }
|
36
37
|
required(:reconnect_timeout).filled { (int? | float?) & gteq?(0) }
|
37
38
|
required(:socket_timeout).filled { (int? | float?) & gt?(0) }
|
@@ -70,13 +71,13 @@ module Karafka
|
|
70
71
|
|
71
72
|
# Uri rule to check if uri is in a Karafka acceptable format
|
72
73
|
rule(:seed_brokers) do
|
73
|
-
if value
|
74
|
+
if value.is_a?(Array) && !value.all?(&method(:kafka_uri?))
|
74
75
|
key.failure(:invalid_broker_schema)
|
75
76
|
end
|
76
77
|
end
|
77
78
|
|
78
79
|
rule(:topics) do
|
79
|
-
if value
|
80
|
+
if value.is_a?(Array)
|
80
81
|
names = value.map { |topic| topic[:name] }
|
81
82
|
|
82
83
|
key.failure(:topics_names_not_unique) if names.size != names.uniq.size
|
@@ -84,7 +85,7 @@ module Karafka
|
|
84
85
|
end
|
85
86
|
|
86
87
|
rule(:topics) do
|
87
|
-
if value
|
88
|
+
if value.is_a?(Array)
|
88
89
|
value.each_with_index do |topic, index|
|
89
90
|
TOPIC_CONTRACT.call(topic).errors.each do |error|
|
90
91
|
key([:topics, index, error.path[0]]).failure(error.text)
|
@@ -93,6 +94,10 @@ module Karafka
|
|
93
94
|
end
|
94
95
|
end
|
95
96
|
|
97
|
+
rule(:assignment_strategy) do
|
98
|
+
key.failure(:does_not_respond_to_call) unless value.respond_to?(:call)
|
99
|
+
end
|
100
|
+
|
96
101
|
rule(:ssl_client_cert, :ssl_client_cert_key) do
|
97
102
|
if values[:ssl_client_cert] && !values[:ssl_client_cert_key]
|
98
103
|
key(:ssl_client_cert_key).failure(:ssl_client_cert_with_ssl_client_cert_key)
|
@@ -178,9 +183,9 @@ module Karafka
|
|
178
183
|
# @param value [String] potential RSA key value
|
179
184
|
# @return [Boolean] is the given string a valid RSA key
|
180
185
|
def valid_private_key?(value)
|
181
|
-
OpenSSL::PKey
|
186
|
+
OpenSSL::PKey.read(value)
|
182
187
|
true
|
183
|
-
rescue OpenSSL::PKey::
|
188
|
+
rescue OpenSSL::PKey::PKeyError
|
184
189
|
false
|
185
190
|
end
|
186
191
|
|