karafka 1.3.0
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 +7 -0
- checksums.yaml.gz.sig +2 -0
- data.tar.gz.sig +0 -0
- data/.coditsu/ci.yml +3 -0
- data/.console_irbrc +11 -0
- data/.github/FUNDING.yml +3 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +50 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.gitignore +69 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +36 -0
- data/CHANGELOG.md +520 -0
- data/CODE_OF_CONDUCT.md +46 -0
- data/CONTRIBUTING.md +41 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +137 -0
- data/MIT-LICENCE +18 -0
- data/README.md +101 -0
- data/bin/karafka +19 -0
- data/certs/mensfeld.pem +25 -0
- data/config/errors.yml +39 -0
- data/karafka.gemspec +44 -0
- data/lib/karafka.rb +71 -0
- data/lib/karafka/app.rb +53 -0
- data/lib/karafka/attributes_map.rb +68 -0
- data/lib/karafka/backends/inline.rb +16 -0
- data/lib/karafka/base_consumer.rb +57 -0
- data/lib/karafka/base_responder.rb +226 -0
- data/lib/karafka/cli.rb +54 -0
- data/lib/karafka/cli/base.rb +78 -0
- data/lib/karafka/cli/console.rb +31 -0
- data/lib/karafka/cli/flow.rb +45 -0
- data/lib/karafka/cli/info.rb +31 -0
- data/lib/karafka/cli/install.rb +64 -0
- data/lib/karafka/cli/server.rb +71 -0
- data/lib/karafka/code_reloader.rb +67 -0
- data/lib/karafka/connection/api_adapter.rb +155 -0
- data/lib/karafka/connection/batch_delegator.rb +51 -0
- data/lib/karafka/connection/builder.rb +16 -0
- data/lib/karafka/connection/client.rb +117 -0
- data/lib/karafka/connection/listener.rb +71 -0
- data/lib/karafka/connection/message_delegator.rb +36 -0
- data/lib/karafka/consumers/callbacks.rb +71 -0
- data/lib/karafka/consumers/includer.rb +63 -0
- data/lib/karafka/consumers/metadata.rb +10 -0
- data/lib/karafka/consumers/responders.rb +24 -0
- data/lib/karafka/consumers/single_params.rb +15 -0
- data/lib/karafka/contracts.rb +10 -0
- data/lib/karafka/contracts/config.rb +21 -0
- data/lib/karafka/contracts/consumer_group.rb +206 -0
- data/lib/karafka/contracts/consumer_group_topic.rb +19 -0
- data/lib/karafka/contracts/responder_usage.rb +54 -0
- data/lib/karafka/contracts/server_cli_options.rb +29 -0
- data/lib/karafka/errors.rb +51 -0
- data/lib/karafka/fetcher.rb +42 -0
- data/lib/karafka/helpers/class_matcher.rb +88 -0
- data/lib/karafka/helpers/config_retriever.rb +46 -0
- data/lib/karafka/helpers/inflector.rb +26 -0
- data/lib/karafka/helpers/multi_delegator.rb +32 -0
- data/lib/karafka/instrumentation/logger.rb +57 -0
- data/lib/karafka/instrumentation/monitor.rb +70 -0
- data/lib/karafka/instrumentation/proctitle_listener.rb +36 -0
- data/lib/karafka/instrumentation/stdout_listener.rb +138 -0
- data/lib/karafka/params/builders/metadata.rb +33 -0
- data/lib/karafka/params/builders/params.rb +36 -0
- data/lib/karafka/params/builders/params_batch.rb +25 -0
- data/lib/karafka/params/metadata.rb +35 -0
- data/lib/karafka/params/params.rb +68 -0
- data/lib/karafka/params/params_batch.rb +61 -0
- data/lib/karafka/patches/ruby_kafka.rb +47 -0
- data/lib/karafka/persistence/client.rb +29 -0
- data/lib/karafka/persistence/consumers.rb +45 -0
- data/lib/karafka/persistence/topics.rb +48 -0
- data/lib/karafka/process.rb +60 -0
- data/lib/karafka/responders/builder.rb +36 -0
- data/lib/karafka/responders/topic.rb +55 -0
- data/lib/karafka/routing/builder.rb +89 -0
- data/lib/karafka/routing/consumer_group.rb +61 -0
- data/lib/karafka/routing/consumer_mapper.rb +34 -0
- data/lib/karafka/routing/proxy.rb +46 -0
- data/lib/karafka/routing/router.rb +29 -0
- data/lib/karafka/routing/topic.rb +62 -0
- data/lib/karafka/routing/topic_mapper.rb +53 -0
- data/lib/karafka/serialization/json/deserializer.rb +27 -0
- data/lib/karafka/serialization/json/serializer.rb +31 -0
- data/lib/karafka/server.rb +83 -0
- data/lib/karafka/setup/config.rb +221 -0
- data/lib/karafka/setup/configurators/water_drop.rb +36 -0
- data/lib/karafka/setup/dsl.rb +21 -0
- data/lib/karafka/status.rb +29 -0
- data/lib/karafka/templates/application_consumer.rb.erb +7 -0
- data/lib/karafka/templates/application_responder.rb.erb +11 -0
- data/lib/karafka/templates/karafka.rb.erb +92 -0
- data/lib/karafka/version.rb +7 -0
- data/log/.gitkeep +0 -0
- metadata +336 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
module Routing
|
5
|
+
# Proxy is used as a translation layer in between the DSL and raw topic and consumer group
|
6
|
+
# objects.
|
7
|
+
class Proxy
|
8
|
+
attr_reader :target
|
9
|
+
|
10
|
+
# We should proxy only non ? and = methods as we want to have a regular dsl
|
11
|
+
IGNORED_POSTFIXES = %w[
|
12
|
+
?
|
13
|
+
=
|
14
|
+
!
|
15
|
+
].freeze
|
16
|
+
|
17
|
+
private_constant :IGNORED_POSTFIXES
|
18
|
+
|
19
|
+
# @param target [Object] target object to which we proxy any DSL call
|
20
|
+
# @param block [Proc] block that we want to evaluate in the proxy context
|
21
|
+
def initialize(target, &block)
|
22
|
+
@target = target
|
23
|
+
instance_eval(&block)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Translates the no "=" DSL of routing into elements assignments on target
|
27
|
+
# @param method_name [Symbol] name of the missing method
|
28
|
+
# @param arguments [Array] array with it's arguments
|
29
|
+
# @param block [Proc] block provided to the method
|
30
|
+
def method_missing(method_name, *arguments, &block)
|
31
|
+
return super unless respond_to_missing?(method_name)
|
32
|
+
|
33
|
+
@target.public_send(:"#{method_name}=", *arguments, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Tells whether or not a given element exists on the target
|
37
|
+
# @param method_name [Symbol] name of the missing method
|
38
|
+
# @param include_private [Boolean] should we include private in the check as well
|
39
|
+
def respond_to_missing?(method_name, include_private = false)
|
40
|
+
return false if IGNORED_POSTFIXES.any? { |postfix| method_name.to_s.end_with?(postfix) }
|
41
|
+
|
42
|
+
@target.respond_to?(:"#{method_name}=", include_private) || super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
# Namespace for all elements related to requests routing
|
5
|
+
module Routing
|
6
|
+
# Karafka framework Router for routing incoming messages to proper consumers
|
7
|
+
# @note Since Kafka does not provide namespaces or modules for topics, they all have "flat"
|
8
|
+
# structure so all the routes are being stored in a single level array
|
9
|
+
module Router
|
10
|
+
# Find a proper topic based on full topic id
|
11
|
+
# @param topic_id [String] proper topic id (already mapped, etc) for which we want to find
|
12
|
+
# routing topic
|
13
|
+
# @return [Karafka::Routing::Route] proper route details
|
14
|
+
# @raise [Karafka::Topic::NonMatchingTopicError] raised if topic name does not match
|
15
|
+
# any route defined by user using routes.draw
|
16
|
+
def find(topic_id)
|
17
|
+
App.consumer_groups.each do |consumer_group|
|
18
|
+
consumer_group.topics.each do |topic|
|
19
|
+
return topic if topic.id == topic_id
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
raise(Errors::NonMatchingRouteError, topic_id)
|
24
|
+
end
|
25
|
+
|
26
|
+
module_function :find
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
module Routing
|
5
|
+
# Topic stores all the details on how we should interact with Kafka given topic
|
6
|
+
# It belongs to a consumer group as from 0.6 all the topics can work in the same consumer group
|
7
|
+
# It is a part of Karafka's DSL
|
8
|
+
class Topic
|
9
|
+
extend Helpers::ConfigRetriever
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
attr_reader :id, :consumer_group
|
13
|
+
attr_accessor :consumer
|
14
|
+
|
15
|
+
def_delegator :@consumer_group, :batch_fetching
|
16
|
+
|
17
|
+
# @param [String, Symbol] name of a topic on which we want to listen
|
18
|
+
# @param consumer_group [Karafka::Routing::ConsumerGroup] owning consumer group of this topic
|
19
|
+
def initialize(name, consumer_group)
|
20
|
+
@name = name.to_s
|
21
|
+
@consumer_group = consumer_group
|
22
|
+
@attributes = {}
|
23
|
+
# @note We use identifier related to the consumer group that owns a topic, because from
|
24
|
+
# Karafka 0.6 we can handle multiple Kafka instances with the same process and we can
|
25
|
+
# have same topic name across multiple Kafkas
|
26
|
+
@id = "#{consumer_group.id}_#{@name}"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Initializes default values for all the options that support defaults if their values are
|
30
|
+
# not yet specified. This is need to be done (cannot be lazy loaded on first use) because
|
31
|
+
# everywhere except Karafka server command, those would not be initialized on time - for
|
32
|
+
# example for Sidekiq
|
33
|
+
def build
|
34
|
+
Karafka::AttributesMap.topic.each { |attr| send(attr) }
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Class, nil] Class (not an instance) of a responder that should respond from
|
39
|
+
# consumer back to Kafka (useful for piping data flows)
|
40
|
+
def responder
|
41
|
+
@responder ||= Karafka::Responders::Builder.new(consumer).build
|
42
|
+
end
|
43
|
+
|
44
|
+
Karafka::AttributesMap.topic.each do |attribute|
|
45
|
+
config_retriever_for(attribute)
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [Hash] hash with all the topic attributes
|
49
|
+
# @note This is being used when we validate the consumer_group and its topics
|
50
|
+
def to_h
|
51
|
+
map = Karafka::AttributesMap.topic.map do |attribute|
|
52
|
+
[attribute, public_send(attribute)]
|
53
|
+
end
|
54
|
+
|
55
|
+
Hash[map].merge!(
|
56
|
+
id: id,
|
57
|
+
consumer: consumer
|
58
|
+
)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
module Routing
|
5
|
+
# Default topic mapper that does not remap things
|
6
|
+
# Mapper can be used for Kafka providers that require namespaced topic names. Instead of being
|
7
|
+
# provider dependent, we can then define mapper and use internally "pure" topic names in
|
8
|
+
# routes and responders
|
9
|
+
#
|
10
|
+
# @example Mapper for mapping prefixed topics
|
11
|
+
# class MyMapper
|
12
|
+
# PREFIX = "my_user_name."
|
13
|
+
#
|
14
|
+
# def incoming(topic)
|
15
|
+
# topic.to_s.gsub(PREFIX, '')
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# def outgoing(topic)
|
19
|
+
# "#{PREFIX}#{topic}"
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# @example Mapper for replacing "." with "_" in topic names
|
24
|
+
# class MyMapper
|
25
|
+
# PREFIX = "my_user_name."
|
26
|
+
#
|
27
|
+
# def incoming(topic)
|
28
|
+
# topic.to_s.gsub('.', '_')
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# def outgoing(topic)
|
32
|
+
# topic.to_s.gsub('_', '.')
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
class TopicMapper
|
36
|
+
# @param topic [String, Symbol] topic
|
37
|
+
# @return [String, Symbol] same topic as on input
|
38
|
+
# @example
|
39
|
+
# incoming('topic_name') #=> 'topic_name'
|
40
|
+
def incoming(topic)
|
41
|
+
topic
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param topic [String, Symbol] topic
|
45
|
+
# @return [String, Symbol] same topic as on input
|
46
|
+
# @example
|
47
|
+
# outgoing('topic_name') #=> 'topic_name'
|
48
|
+
def outgoing(topic)
|
49
|
+
topic
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
# Module for all supported by default serialization and deserialization ways
|
5
|
+
module Serialization
|
6
|
+
# Namespace for json ser/der
|
7
|
+
module Json
|
8
|
+
# Default Karafka Json deserializer for loading JSON data
|
9
|
+
class Deserializer
|
10
|
+
# @param params [Karafka::Params::Params] Full params object that we want to deserialize
|
11
|
+
# @return [Hash] hash with deserialized JSON data
|
12
|
+
# @example
|
13
|
+
# params = {
|
14
|
+
# 'payload' => "{\"a\":1}",
|
15
|
+
# 'topic' => 'my-topic',
|
16
|
+
# 'headers' => { 'message_type' => :test }
|
17
|
+
# }
|
18
|
+
# Deserializer.call(params) #=> { 'a' => 1 }
|
19
|
+
def call(params)
|
20
|
+
::MultiJson.load(params['payload'])
|
21
|
+
rescue ::MultiJson::ParseError => e
|
22
|
+
raise ::Karafka::Errors::DeserializationError, e
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
# Module for all supported by default serialization and deserialization ways
|
5
|
+
module Serialization
|
6
|
+
module Json
|
7
|
+
# Default Karafka Json serializer for serializing data
|
8
|
+
class Serializer
|
9
|
+
# @param content [Object] any object that we want to convert to a json string
|
10
|
+
# @return [String] Valid JSON string containing serialized data
|
11
|
+
# @raise [Karafka::Errors::SerializationError] raised when we don't have a way to
|
12
|
+
# serialize provided data to json
|
13
|
+
# @note When string is passed to this method, we assume that it is already a json
|
14
|
+
# string and we don't serialize it again. This allows us to serialize data before
|
15
|
+
# it is being forwarded to this serializer if we want to have a custom (not that simple)
|
16
|
+
# json serialization
|
17
|
+
#
|
18
|
+
# @example From an ActiveRecord object
|
19
|
+
# Serializer.call(Repository.first) #=> "{\"repository\":{\"id\":\"04b504e0\"}}"
|
20
|
+
# @example From a string (no changes)
|
21
|
+
# Serializer.call("{\"a\":1}") #=> "{\"a\":1}"
|
22
|
+
def call(content)
|
23
|
+
return content if content.is_a?(String)
|
24
|
+
return content.to_json if content.respond_to?(:to_json)
|
25
|
+
|
26
|
+
raise Karafka::Errors::SerializationError, content
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
# Karafka consuming server class
|
5
|
+
class Server
|
6
|
+
@consumer_threads = Concurrent::Array.new
|
7
|
+
|
8
|
+
# How long should we sleep between checks on shutting down consumers
|
9
|
+
SUPERVISION_SLEEP = 0.1
|
10
|
+
# What system exit code should we use when we terminated forcefully
|
11
|
+
FORCEFUL_EXIT_CODE = 2
|
12
|
+
# This factor allows us to calculate how many times we have to sleep before
|
13
|
+
# a forceful shutdown
|
14
|
+
SUPERVISION_CHECK_FACTOR = (1 / SUPERVISION_SLEEP)
|
15
|
+
|
16
|
+
private_constant :SUPERVISION_SLEEP, :FORCEFUL_EXIT_CODE, :SUPERVISION_CHECK_FACTOR
|
17
|
+
|
18
|
+
class << self
|
19
|
+
# Set of consuming threads. Each consumer thread contains a single consumer
|
20
|
+
attr_accessor :consumer_threads
|
21
|
+
|
22
|
+
# Writer for list of consumer groups that we want to consume in our current process context
|
23
|
+
attr_writer :consumer_groups
|
24
|
+
|
25
|
+
# Method which runs app
|
26
|
+
def run
|
27
|
+
process.on_sigint { stop_supervised }
|
28
|
+
process.on_sigquit { stop_supervised }
|
29
|
+
process.on_sigterm { stop_supervised }
|
30
|
+
run_supervised
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Array<String>] array with names of consumer groups that should be consumed in a
|
34
|
+
# current server context
|
35
|
+
def consumer_groups
|
36
|
+
# If not specified, a server will listed on all the topics
|
37
|
+
@consumer_groups ||= Karafka::App.consumer_groups.map(&:name).freeze
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# @return [Karafka::Process] process wrapper instance used to catch system signal calls
|
43
|
+
def process
|
44
|
+
Karafka::App.config.internal.process
|
45
|
+
end
|
46
|
+
|
47
|
+
# Starts Karafka with a supervision
|
48
|
+
# @note We don't need to sleep because Karafka::Fetcher is locking and waiting to
|
49
|
+
# finish loop (and it won't happen until we explicitly want to stop)
|
50
|
+
def run_supervised
|
51
|
+
process.supervise
|
52
|
+
Karafka::App.run!
|
53
|
+
Karafka::App.config.internal.fetcher.call
|
54
|
+
end
|
55
|
+
|
56
|
+
# Stops Karafka with a supervision (as long as there is a shutdown timeout)
|
57
|
+
# If consumers won't stop in a given time frame, it will force them to exit
|
58
|
+
def stop_supervised
|
59
|
+
Karafka::App.stop!
|
60
|
+
|
61
|
+
# We check from time to time (for the timeout period) if all the threads finished
|
62
|
+
# their work and if so, we can just return and normal shutdown process will take place
|
63
|
+
(Karafka::App.config.shutdown_timeout * SUPERVISION_CHECK_FACTOR).to_i.times do
|
64
|
+
if consumer_threads.count(&:alive?).zero?
|
65
|
+
Thread.new { Karafka.monitor.instrument('app.stopped') }.join
|
66
|
+
return
|
67
|
+
end
|
68
|
+
|
69
|
+
sleep SUPERVISION_SLEEP
|
70
|
+
end
|
71
|
+
|
72
|
+
raise Errors::ForcefulShutdownError
|
73
|
+
rescue Errors::ForcefulShutdownError => e
|
74
|
+
Thread.new { Karafka.monitor.instrument('app.stopping.error', error: e) }.join
|
75
|
+
# We're done waiting, lets kill them!
|
76
|
+
consumer_threads.each(&:terminate)
|
77
|
+
|
78
|
+
# exit! is not within the instrumentation as it would not trigger due to exit
|
79
|
+
Kernel.exit! FORCEFUL_EXIT_CODE
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
# Module containing all Karafka setup related elements like configuration settings,
|
5
|
+
# config validations and configurators for external gems integration
|
6
|
+
module Setup
|
7
|
+
# Configurator for setting up all the framework details that are required to make it work
|
8
|
+
# @note If you want to do some configurations after all of this is done, please add to
|
9
|
+
# karafka/config a proper file (needs to inherit from Karafka::Setup::Configurators::Base
|
10
|
+
# and implement setup method) after that everything will happen automatically
|
11
|
+
# @note This config object allows to create a 1 level nesting (nodes) only. This should be
|
12
|
+
# enough and will still keep the code simple
|
13
|
+
# @see Karafka::Setup::Configurators::Base for more details about configurators api
|
14
|
+
class Config
|
15
|
+
extend Dry::Configurable
|
16
|
+
|
17
|
+
# Contract for checking the config provided by the user
|
18
|
+
CONTRACT = Karafka::Contracts::Config.new.freeze
|
19
|
+
|
20
|
+
private_constant :CONTRACT
|
21
|
+
|
22
|
+
# Available settings
|
23
|
+
# option client_id [String] kafka client_id - used to provide
|
24
|
+
# default Kafka groups namespaces and identify that app in kafka
|
25
|
+
setting :client_id
|
26
|
+
# What backend do we want to use to process messages
|
27
|
+
setting :backend, :inline
|
28
|
+
# option logger [Instance] logger that we want to use
|
29
|
+
setting :logger, ::Karafka::Instrumentation::Logger.new
|
30
|
+
# option monitor [Instance] monitor that we will to use (defaults to Karafka::Monitor)
|
31
|
+
setting :monitor, ::Karafka::Instrumentation::Monitor.new
|
32
|
+
# Mapper used to remap consumer groups ids, so in case users migrate from other tools
|
33
|
+
# or they need to maintain their own internal consumer group naming conventions, they
|
34
|
+
# can easily do it, replacing the default client_id + consumer name pattern concept
|
35
|
+
setting :consumer_mapper, Routing::ConsumerMapper.new
|
36
|
+
# Mapper used to remap names of topics, so we can have a clean internal topic naming
|
37
|
+
# despite using any Kafka provider that uses namespacing, etc
|
38
|
+
# It needs to implement two methods:
|
39
|
+
# - #incoming - for remapping from the incoming message to our internal format
|
40
|
+
# - #outgoing - for remapping from internal topic name into outgoing message
|
41
|
+
setting :topic_mapper, Routing::TopicMapper.new
|
42
|
+
# Default serializer for converting whatever we want to send to kafka to json
|
43
|
+
setting :serializer, Karafka::Serialization::Json::Serializer.new
|
44
|
+
# Default deserializer for converting incoming data into ruby objects
|
45
|
+
setting :deserializer, Karafka::Serialization::Json::Deserializer.new
|
46
|
+
# If batch_fetching is true, we will fetch kafka messages in batches instead of 1 by 1
|
47
|
+
# @note Fetching does not equal consuming, see batch_consuming description for details
|
48
|
+
setting :batch_fetching, true
|
49
|
+
# If batch_consuming is true, we will have access to #params_batch instead of #params.
|
50
|
+
# #params_batch will contain params received from Kafka (may be more than 1) so we can
|
51
|
+
# process them in batches
|
52
|
+
setting :batch_consuming, false
|
53
|
+
# option shutdown_timeout [Integer, nil] the number of seconds after which Karafka no
|
54
|
+
# longer wait for the consumers to stop gracefully but instead we force terminate
|
55
|
+
# everything.
|
56
|
+
setting :shutdown_timeout, 60
|
57
|
+
|
58
|
+
# option kafka [Hash] - optional - kafka configuration options
|
59
|
+
setting :kafka do
|
60
|
+
# Array with at least one host
|
61
|
+
setting :seed_brokers, %w[kafka://127.0.0.1:9092]
|
62
|
+
# option session_timeout [Integer] the number of seconds after which, if a client
|
63
|
+
# hasn't contacted the Kafka cluster, it will be kicked out of the group.
|
64
|
+
setting :session_timeout, 30
|
65
|
+
# Time that a given partition will be paused from fetching messages, when message
|
66
|
+
# consumption fails. It allows us to process other partitions, while the error is being
|
67
|
+
# resolved and also "slows" things down, so it prevents from "eating" up all messages and
|
68
|
+
# consuming them with failed code. Use `nil` if you want to pause forever and never retry.
|
69
|
+
setting :pause_timeout, 10
|
70
|
+
# option pause_max_timeout [Integer, nil] the maximum number of seconds to pause for,
|
71
|
+
# or `nil` if no maximum should be enforced.
|
72
|
+
setting :pause_max_timeout, nil
|
73
|
+
# option pause_exponential_backoff [Boolean] whether to enable exponential backoff
|
74
|
+
setting :pause_exponential_backoff, false
|
75
|
+
# option offset_commit_interval [Integer] the interval between offset commits,
|
76
|
+
# in seconds.
|
77
|
+
setting :offset_commit_interval, 10
|
78
|
+
# option offset_commit_threshold [Integer] the number of messages that can be
|
79
|
+
# processed before their offsets are committed. If zero, offset commits are
|
80
|
+
# not triggered by message consumption.
|
81
|
+
setting :offset_commit_threshold, 0
|
82
|
+
# option heartbeat_interval [Integer] the interval between heartbeats; must be less
|
83
|
+
# than the session window.
|
84
|
+
setting :heartbeat_interval, 10
|
85
|
+
# option offset_retention_time [Integer] The length of the retention window, known as
|
86
|
+
# offset retention time
|
87
|
+
setting :offset_retention_time, nil
|
88
|
+
# option fetcher_max_queue_size [Integer] max number of items in the fetch queue that
|
89
|
+
# are stored for further processing. Note, that each item in the queue represents a
|
90
|
+
# response from a single broker
|
91
|
+
setting :fetcher_max_queue_size, 10
|
92
|
+
# option max_bytes_per_partition [Integer] the maximum amount of data fetched
|
93
|
+
# from a single partition at a time.
|
94
|
+
setting :max_bytes_per_partition, 1_048_576
|
95
|
+
# whether to consume messages starting at the beginning or to just consume new messages
|
96
|
+
setting :start_from_beginning, true
|
97
|
+
# option min_bytes [Integer] the minimum number of bytes to read before
|
98
|
+
# returning messages from the server; if `max_wait_time` is reached, this
|
99
|
+
# is ignored.
|
100
|
+
setting :min_bytes, 1
|
101
|
+
# option max_bytes [Integer] the maximum number of bytes to read before returning messages
|
102
|
+
# from each broker.
|
103
|
+
setting :max_bytes, 10_485_760
|
104
|
+
# option max_wait_time [Integer, Float] max_wait_time is the maximum number of seconds to
|
105
|
+
# wait before returning data from a single message fetch. By setting this high you also
|
106
|
+
# increase the fetching throughput - and by setting it low you set a bound on latency.
|
107
|
+
# This configuration overrides `min_bytes`, so you'll _always_ get data back within the
|
108
|
+
# time specified. The default value is one second. If you want to have at most five
|
109
|
+
# seconds of latency, set `max_wait_time` to 5. You should make sure
|
110
|
+
# max_wait_time * num brokers + heartbeat_interval is less than session_timeout.
|
111
|
+
setting :max_wait_time, 1
|
112
|
+
# option automatically_mark_as_consumed [Boolean] should we automatically mark received
|
113
|
+
# messages as consumed (processed) after non-error consumption
|
114
|
+
setting :automatically_mark_as_consumed, true
|
115
|
+
# option reconnect_timeout [Integer] How long should we wait before trying to reconnect to
|
116
|
+
# Kafka cluster that went down (in seconds)
|
117
|
+
setting :reconnect_timeout, 5
|
118
|
+
# option connect_timeout [Integer] Sets the number of seconds to wait while connecting to
|
119
|
+
# a broker for the first time. When ruby-kafka initializes, it needs to connect to at
|
120
|
+
# least one host.
|
121
|
+
setting :connect_timeout, 10
|
122
|
+
# option socket_timeout [Integer] Sets the number of seconds to wait when reading from or
|
123
|
+
# writing to a socket connection to a broker. After this timeout expires the connection
|
124
|
+
# will be killed. Note that some Kafka operations are by definition long-running, such as
|
125
|
+
# waiting for new messages to arrive in a partition, so don't set this value too low
|
126
|
+
setting :socket_timeout, 30
|
127
|
+
|
128
|
+
# SSL authentication related settings
|
129
|
+
# option ca_cert [String, nil] SSL CA certificate
|
130
|
+
setting :ssl_ca_cert, nil
|
131
|
+
# option ssl_ca_cert_file_path [String, nil] SSL CA certificate file path
|
132
|
+
setting :ssl_ca_cert_file_path, nil
|
133
|
+
# option ssl_ca_certs_from_system [Boolean] Use the CA certs from your system's default
|
134
|
+
# certificate store
|
135
|
+
setting :ssl_ca_certs_from_system, false
|
136
|
+
# option ssl_verify_hostname [Boolean] Verify the hostname for client certs
|
137
|
+
setting :ssl_verify_hostname, true
|
138
|
+
# option ssl_client_cert [String, nil] SSL client certificate
|
139
|
+
setting :ssl_client_cert, nil
|
140
|
+
# option ssl_client_cert_key [String, nil] SSL client certificate password
|
141
|
+
setting :ssl_client_cert_key, nil
|
142
|
+
# option sasl_gssapi_principal [String, nil] sasl principal
|
143
|
+
setting :sasl_gssapi_principal, nil
|
144
|
+
# option sasl_gssapi_keytab [String, nil] sasl keytab
|
145
|
+
setting :sasl_gssapi_keytab, nil
|
146
|
+
# option sasl_plain_authzid [String] The authorization identity to use
|
147
|
+
setting :sasl_plain_authzid, ''
|
148
|
+
# option sasl_plain_username [String, nil] The username used to authenticate
|
149
|
+
setting :sasl_plain_username, nil
|
150
|
+
# option sasl_plain_password [String, nil] The password used to authenticate
|
151
|
+
setting :sasl_plain_password, nil
|
152
|
+
# option sasl_scram_username [String, nil] The username used to authenticate
|
153
|
+
setting :sasl_scram_username, nil
|
154
|
+
# option sasl_scram_password [String, nil] The password used to authenticate
|
155
|
+
setting :sasl_scram_password, nil
|
156
|
+
# option sasl_scram_mechanism [String, nil] Scram mechanism, either 'sha256' or 'sha512'
|
157
|
+
setting :sasl_scram_mechanism, nil
|
158
|
+
# option sasl_over_ssl [Boolean] whether to enforce SSL with SASL
|
159
|
+
setting :sasl_over_ssl, true
|
160
|
+
# option ssl_client_cert_chain [String, nil] client cert chain or nil if not used
|
161
|
+
setting :ssl_client_cert_chain, nil
|
162
|
+
# option ssl_client_cert_key_password [String, nil] the password required to read
|
163
|
+
# the ssl_client_cert_key
|
164
|
+
setting :ssl_client_cert_key_password, nil
|
165
|
+
# @param sasl_oauth_token_provider [Object, nil] OAuthBearer Token Provider instance that
|
166
|
+
# implements method token.
|
167
|
+
setting :sasl_oauth_token_provider, nil
|
168
|
+
end
|
169
|
+
|
170
|
+
# Namespace for internal settings that should not be modified
|
171
|
+
# It's a temporary step to "declassify" several things internally before we move to a
|
172
|
+
# non global state
|
173
|
+
setting :internal do
|
174
|
+
# option routing_builder [Karafka::Routing::Builder] builder instance
|
175
|
+
setting :routing_builder, Routing::Builder.new
|
176
|
+
# option status [Karafka::Status] app status
|
177
|
+
setting :status, Status.new
|
178
|
+
# option process [Karafka::Process] process status
|
179
|
+
# @note In the future, we need to have a single process representation for all the karafka
|
180
|
+
# instances
|
181
|
+
setting :process, Process.new
|
182
|
+
# option fetcher [Karafka::Fetcher] fetcher instance
|
183
|
+
setting :fetcher, Fetcher.new
|
184
|
+
# option configurators [Array<Object>] all configurators that we want to run after
|
185
|
+
# the setup
|
186
|
+
setting :configurators, [Configurators::WaterDrop.new]
|
187
|
+
end
|
188
|
+
|
189
|
+
class << self
|
190
|
+
# Configuring method
|
191
|
+
# @yield Runs a block of code providing a config singleton instance to it
|
192
|
+
# @yieldparam [Karafka::Setup::Config] Karafka config instance
|
193
|
+
def setup
|
194
|
+
configure { |config| yield(config) }
|
195
|
+
end
|
196
|
+
|
197
|
+
# Everything that should be initialized after the setup
|
198
|
+
# Components are in karafka/config directory and are all loaded one by one
|
199
|
+
# If you want to configure a next component, please add a proper file to config dir
|
200
|
+
def setup_components
|
201
|
+
config
|
202
|
+
.internal
|
203
|
+
.configurators
|
204
|
+
.each { |configurator| configurator.call(config) }
|
205
|
+
end
|
206
|
+
|
207
|
+
# Validate config based on the config contract
|
208
|
+
# @return [Boolean] true if configuration is valid
|
209
|
+
# @raise [Karafka::Errors::InvalidConfigurationError] raised when configuration
|
210
|
+
# doesn't match with the config contract
|
211
|
+
def validate!
|
212
|
+
validation_result = CONTRACT.call(config.to_h)
|
213
|
+
|
214
|
+
return true if validation_result.success?
|
215
|
+
|
216
|
+
raise Errors::InvalidConfigurationError, validation_result.errors.to_h
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|