karafka 2.0.0 → 2.1.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/ci.yml +40 -16
- data/.rspec +4 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +459 -796
- data/Gemfile +3 -2
- data/Gemfile.lock +44 -32
- data/LICENSE +1 -1
- data/README.md +22 -17
- data/bin/integrations +86 -26
- data/bin/karafka +1 -13
- data/bin/rspecs +6 -0
- data/bin/verify_license_integrity +37 -0
- data/certs/cert_chain.pem +26 -0
- data/config/{errors.yml → locales/errors.yml} +34 -6
- data/config/locales/pro_errors.yml +34 -0
- data/docker-compose.yml +4 -30
- data/karafka.gemspec +17 -10
- data/lib/active_job/karafka.rb +3 -7
- data/lib/active_job/queue_adapters/karafka_adapter.rb +7 -1
- data/lib/karafka/active_job/consumer.rb +29 -6
- data/lib/karafka/active_job/current_attributes/loading.rb +36 -0
- data/lib/karafka/active_job/current_attributes/persistence.rb +28 -0
- data/lib/karafka/active_job/current_attributes.rb +42 -0
- data/lib/karafka/active_job/dispatcher.rb +34 -3
- data/lib/karafka/active_job/job_options_contract.rb +13 -2
- data/lib/karafka/admin.rb +246 -0
- data/lib/karafka/app.rb +30 -7
- data/lib/karafka/base_consumer.rb +124 -82
- data/lib/karafka/cli/base.rb +20 -0
- data/lib/karafka/cli/console.rb +13 -8
- data/lib/karafka/cli/info.rb +3 -2
- data/lib/karafka/cli/server.rb +55 -8
- data/lib/karafka/cli/topics.rb +146 -0
- data/lib/karafka/connection/client.rb +118 -61
- data/lib/karafka/connection/consumer_group_coordinator.rb +48 -0
- data/lib/karafka/connection/listener.rb +78 -30
- data/lib/karafka/connection/listeners_batch.rb +18 -2
- data/lib/karafka/connection/rebalance_manager.rb +3 -0
- data/lib/karafka/contracts/config.rb +1 -1
- data/lib/karafka/contracts/consumer_group.rb +22 -9
- data/lib/karafka/contracts/server_cli_options.rb +71 -11
- data/lib/karafka/contracts/topic.rb +65 -0
- data/lib/karafka/contracts.rb +1 -1
- data/lib/karafka/embedded.rb +36 -0
- data/lib/karafka/errors.rb +6 -4
- data/lib/karafka/helpers/colorize.rb +6 -0
- data/lib/karafka/instrumentation/callbacks/error.rb +3 -4
- data/lib/karafka/instrumentation/callbacks/statistics.rb +14 -4
- data/lib/karafka/instrumentation/logger_listener.rb +151 -22
- data/lib/karafka/instrumentation/notifications.rb +26 -7
- data/lib/karafka/instrumentation/proctitle_listener.rb +7 -16
- data/lib/karafka/instrumentation/vendors/datadog/dashboard.json +1 -1
- data/lib/karafka/instrumentation/vendors/datadog/logger_listener.rb +153 -0
- data/lib/karafka/instrumentation/vendors/datadog/{listener.rb → metrics_listener.rb} +62 -35
- data/lib/karafka/instrumentation/vendors/kubernetes/liveness_listener.rb +166 -0
- data/lib/karafka/licenser.rb +53 -50
- data/lib/karafka/messages/builders/batch_metadata.rb +8 -8
- data/lib/karafka/messages/builders/messages.rb +6 -2
- data/lib/karafka/messages/message.rb +14 -2
- data/lib/karafka/messages/messages.rb +5 -0
- data/lib/karafka/messages/parser.rb +14 -0
- data/lib/karafka/patches/rdkafka/bindings.rb +132 -0
- data/lib/karafka/pro/active_job/consumer.rb +10 -9
- data/lib/karafka/pro/active_job/dispatcher.rb +28 -3
- data/lib/karafka/pro/active_job/job_options_contract.rb +16 -3
- data/lib/karafka/pro/encryption/cipher.rb +58 -0
- data/lib/karafka/pro/encryption/contracts/config.rb +79 -0
- data/lib/karafka/pro/encryption/errors.rb +24 -0
- data/lib/karafka/pro/encryption/messages/middleware.rb +46 -0
- data/lib/karafka/pro/encryption/messages/parser.rb +56 -0
- data/lib/karafka/pro/encryption/setup/config.rb +48 -0
- data/lib/karafka/pro/encryption.rb +47 -0
- data/lib/karafka/pro/iterator.rb +253 -0
- data/lib/karafka/pro/loader.rb +52 -26
- data/lib/karafka/pro/performance_tracker.rb +10 -6
- data/lib/karafka/pro/processing/collapser.rb +62 -0
- data/lib/karafka/pro/processing/coordinator.rb +92 -16
- data/lib/karafka/pro/processing/filters/base.rb +61 -0
- data/lib/karafka/pro/processing/filters/delayer.rb +70 -0
- data/lib/karafka/pro/processing/filters/expirer.rb +51 -0
- data/lib/karafka/pro/processing/filters/throttler.rb +84 -0
- data/lib/karafka/pro/processing/filters/virtual_limiter.rb +52 -0
- data/lib/karafka/pro/processing/filters_applier.rb +104 -0
- data/lib/karafka/pro/processing/jobs/consume_non_blocking.rb +7 -5
- data/lib/karafka/pro/processing/jobs/revoked_non_blocking.rb +37 -0
- data/lib/karafka/pro/processing/jobs_builder.rb +22 -4
- data/lib/karafka/pro/processing/partitioner.rb +33 -24
- data/lib/karafka/pro/processing/scheduler.rb +27 -8
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_lrj_mom.rb +68 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_lrj_mom_vp.rb +74 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_mom.rb +72 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_mom_vp.rb +76 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_lrj_mom.rb +64 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_lrj_mom_vp.rb +68 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_mom.rb +64 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_mom_vp.rb +69 -0
- data/lib/karafka/pro/processing/strategies/aj/ftr_lrj_mom.rb +38 -0
- data/lib/karafka/pro/processing/strategies/aj/ftr_lrj_mom_vp.rb +64 -0
- data/lib/karafka/pro/processing/strategies/aj/ftr_mom.rb +38 -0
- data/lib/karafka/pro/processing/strategies/aj/ftr_mom_vp.rb +58 -0
- data/lib/karafka/pro/processing/strategies/aj/lrj_mom.rb +37 -0
- data/lib/karafka/pro/processing/strategies/aj/lrj_mom_vp.rb +79 -0
- data/lib/karafka/pro/processing/strategies/aj/mom.rb +36 -0
- data/lib/karafka/pro/processing/strategies/aj/mom_vp.rb +52 -0
- data/lib/karafka/pro/processing/strategies/base.rb +26 -0
- data/lib/karafka/pro/processing/strategies/default.rb +105 -0
- data/lib/karafka/pro/processing/strategies/dlq/default.rb +131 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr.rb +61 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj.rb +75 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_mom.rb +71 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_mom_vp.rb +43 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_vp.rb +41 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_mom.rb +69 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_mom_vp.rb +41 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_vp.rb +40 -0
- data/lib/karafka/pro/processing/strategies/dlq/lrj.rb +63 -0
- data/lib/karafka/pro/processing/strategies/dlq/lrj_mom.rb +63 -0
- data/lib/karafka/pro/processing/strategies/dlq/lrj_mom_vp.rb +36 -0
- data/lib/karafka/pro/processing/strategies/dlq/lrj_vp.rb +39 -0
- data/lib/karafka/pro/processing/strategies/dlq/mom.rb +68 -0
- data/lib/karafka/pro/processing/strategies/dlq/mom_vp.rb +37 -0
- data/lib/karafka/pro/processing/strategies/dlq/vp.rb +40 -0
- data/lib/karafka/pro/processing/strategies/ftr/default.rb +104 -0
- data/lib/karafka/pro/processing/strategies/ftr/vp.rb +40 -0
- data/lib/karafka/pro/processing/strategies/lrj/default.rb +87 -0
- data/lib/karafka/pro/processing/strategies/lrj/ftr.rb +69 -0
- data/lib/karafka/pro/processing/strategies/lrj/ftr_mom.rb +67 -0
- data/lib/karafka/pro/processing/strategies/lrj/ftr_mom_vp.rb +40 -0
- data/lib/karafka/pro/processing/strategies/lrj/ftr_vp.rb +39 -0
- data/lib/karafka/pro/processing/strategies/lrj/mom.rb +80 -0
- data/lib/karafka/pro/processing/strategies/lrj/mom_vp.rb +38 -0
- data/lib/karafka/pro/processing/strategies/lrj/vp.rb +36 -0
- data/lib/karafka/pro/processing/strategies/mom/default.rb +46 -0
- data/lib/karafka/pro/processing/strategies/mom/ftr.rb +53 -0
- data/lib/karafka/pro/processing/strategies/mom/ftr_vp.rb +37 -0
- data/lib/karafka/pro/processing/strategies/mom/vp.rb +35 -0
- data/lib/karafka/pro/processing/strategies/vp/default.rb +104 -0
- data/lib/karafka/pro/{contracts/base.rb → processing/strategies.rb} +6 -5
- data/lib/karafka/pro/processing/strategy_selector.rb +84 -0
- data/lib/karafka/pro/processing/virtual_offset_manager.rb +147 -0
- data/lib/karafka/pro/routing/features/base.rb +24 -0
- data/lib/karafka/pro/routing/features/dead_letter_queue/contract.rb +50 -0
- data/lib/karafka/pro/routing/features/dead_letter_queue.rb +27 -0
- data/lib/karafka/pro/routing/{builder_extensions.rb → features/delaying/config.rb} +8 -11
- data/lib/karafka/pro/routing/features/delaying/contract.rb +38 -0
- data/lib/karafka/pro/routing/features/delaying/topic.rb +59 -0
- data/lib/karafka/pro/routing/features/delaying.rb +29 -0
- data/lib/karafka/pro/routing/features/expiring/config.rb +27 -0
- data/lib/karafka/pro/routing/features/expiring/contract.rb +38 -0
- data/lib/karafka/pro/routing/features/expiring/topic.rb +59 -0
- data/lib/karafka/pro/routing/features/expiring.rb +27 -0
- data/lib/karafka/pro/routing/features/filtering/config.rb +40 -0
- data/lib/karafka/pro/routing/features/filtering/contract.rb +41 -0
- data/lib/karafka/pro/routing/features/filtering/topic.rb +51 -0
- data/lib/karafka/pro/routing/features/filtering.rb +27 -0
- data/lib/karafka/pro/routing/features/long_running_job/config.rb +28 -0
- data/lib/karafka/pro/routing/features/long_running_job/contract.rb +37 -0
- data/lib/karafka/pro/routing/features/long_running_job/topic.rb +42 -0
- data/lib/karafka/pro/routing/features/long_running_job.rb +28 -0
- data/lib/karafka/pro/routing/features/throttling/config.rb +32 -0
- data/lib/karafka/pro/routing/features/throttling/contract.rb +41 -0
- data/lib/karafka/pro/routing/features/throttling/topic.rb +69 -0
- data/lib/karafka/pro/routing/features/throttling.rb +30 -0
- data/lib/karafka/pro/routing/features/virtual_partitions/config.rb +30 -0
- data/lib/karafka/pro/routing/features/virtual_partitions/contract.rb +52 -0
- data/lib/karafka/pro/routing/features/virtual_partitions/topic.rb +56 -0
- data/lib/karafka/pro/routing/features/virtual_partitions.rb +27 -0
- data/lib/karafka/process.rb +23 -8
- data/lib/karafka/processing/coordinator.rb +102 -23
- data/lib/karafka/processing/coordinators_buffer.rb +5 -1
- data/lib/karafka/processing/executor.rb +51 -21
- data/lib/karafka/processing/executors_buffer.rb +10 -26
- data/lib/karafka/processing/jobs/base.rb +6 -2
- data/lib/karafka/processing/jobs/consume.rb +8 -5
- data/lib/karafka/processing/jobs/idle.rb +24 -0
- data/lib/karafka/processing/jobs_builder.rb +2 -3
- data/lib/karafka/processing/jobs_queue.rb +14 -8
- data/lib/karafka/processing/partitioner.rb +3 -1
- data/lib/karafka/processing/result.rb +14 -1
- data/lib/karafka/processing/strategies/aj_dlq_mom.rb +44 -0
- data/lib/karafka/processing/strategies/aj_mom.rb +21 -0
- data/lib/karafka/processing/strategies/base.rb +52 -0
- data/lib/karafka/processing/strategies/default.rb +142 -0
- data/lib/karafka/processing/strategies/dlq.rb +88 -0
- data/lib/karafka/processing/strategies/dlq_mom.rb +49 -0
- data/lib/karafka/processing/strategies/mom.rb +29 -0
- data/lib/karafka/processing/strategy_selector.rb +47 -0
- data/lib/karafka/processing/worker.rb +8 -3
- data/lib/karafka/railtie.rb +24 -12
- data/lib/karafka/routing/activity_manager.rb +84 -0
- data/lib/karafka/routing/builder.rb +20 -0
- data/lib/karafka/routing/consumer_group.rb +38 -4
- data/lib/karafka/routing/consumer_mapper.rb +0 -10
- data/lib/karafka/routing/features/active_job/builder.rb +33 -0
- data/lib/karafka/routing/features/active_job/config.rb +15 -0
- data/lib/karafka/routing/features/active_job/contract.rb +41 -0
- data/lib/karafka/routing/features/active_job/topic.rb +33 -0
- data/lib/karafka/routing/features/active_job.rb +13 -0
- data/lib/karafka/routing/features/base/expander.rb +53 -0
- data/lib/karafka/routing/features/base.rb +34 -0
- data/lib/karafka/routing/features/dead_letter_queue/config.rb +19 -0
- data/lib/karafka/routing/features/dead_letter_queue/contract.rb +42 -0
- data/lib/karafka/routing/features/dead_letter_queue/topic.rb +41 -0
- data/lib/karafka/routing/features/dead_letter_queue.rb +16 -0
- data/lib/karafka/routing/features/declaratives/config.rb +18 -0
- data/lib/karafka/routing/features/declaratives/contract.rb +30 -0
- data/lib/karafka/routing/features/declaratives/topic.rb +44 -0
- data/lib/karafka/routing/features/declaratives.rb +14 -0
- data/lib/karafka/routing/features/manual_offset_management/config.rb +15 -0
- data/lib/karafka/routing/features/manual_offset_management/contract.rb +24 -0
- data/lib/karafka/routing/features/manual_offset_management/topic.rb +35 -0
- data/lib/karafka/routing/features/manual_offset_management.rb +18 -0
- data/lib/karafka/routing/proxy.rb +18 -20
- data/lib/karafka/routing/router.rb +27 -2
- data/lib/karafka/routing/subscription_group.rb +43 -5
- data/lib/karafka/routing/subscription_groups_builder.rb +6 -1
- data/lib/karafka/routing/topic.rb +17 -5
- data/lib/karafka/routing/topics.rb +8 -0
- data/lib/karafka/runner.rb +1 -0
- data/lib/karafka/serialization/json/deserializer.rb +1 -0
- data/lib/karafka/server.rb +58 -28
- data/lib/karafka/setup/attributes_map.rb +344 -0
- data/lib/karafka/setup/config.rb +48 -19
- data/lib/karafka/status.rb +35 -7
- data/lib/karafka/templates/karafka.rb.erb +14 -6
- data/lib/karafka/time_trackers/base.rb +1 -6
- data/lib/karafka/time_trackers/pause.rb +49 -19
- data/lib/karafka/time_trackers/poll.rb +7 -3
- data/lib/karafka/version.rb +1 -1
- data/lib/karafka.rb +56 -3
- data/renovate.json +6 -0
- data.tar.gz.sig +0 -0
- metadata +189 -61
- metadata.gz.sig +0 -0
- data/bin/wait_for_kafka +0 -20
- data/certs/mensfeld.pem +0 -25
- data/lib/karafka/active_job/routing/extensions.rb +0 -31
- data/lib/karafka/contracts/consumer_group_topic.rb +0 -42
- data/lib/karafka/instrumentation.rb +0 -21
- data/lib/karafka/pro/base_consumer.rb +0 -82
- data/lib/karafka/pro/contracts/consumer_group.rb +0 -34
- data/lib/karafka/pro/contracts/consumer_group_topic.rb +0 -33
- data/lib/karafka/pro/routing/topic_extensions.rb +0 -38
|
@@ -7,15 +7,6 @@ module Karafka
|
|
|
7
7
|
class Proxy
|
|
8
8
|
attr_reader :target
|
|
9
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
10
|
# @param target [Object] target object to which we proxy any DSL call
|
|
20
11
|
# @param block [Proc] block that we want to evaluate in the proxy context
|
|
21
12
|
def initialize(target, &block)
|
|
@@ -23,23 +14,30 @@ module Karafka
|
|
|
23
14
|
instance_eval(&block)
|
|
24
15
|
end
|
|
25
16
|
|
|
26
|
-
#
|
|
27
|
-
|
|
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)
|
|
17
|
+
# Ruby 2.7.0 to 2.7.2 do not have arg forwarding, so we fallback to the old way
|
|
18
|
+
arg_forwarding = RUBY_VERSION < '3.0' ? '*args, &block' : '...'
|
|
32
19
|
|
|
33
|
-
|
|
34
|
-
|
|
20
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
21
|
+
# Translates the no "=" DSL of routing into elements assignments on target
|
|
22
|
+
# @param method_name [Symbol] name of the missing method
|
|
23
|
+
def method_missing(method_name, #{arg_forwarding})
|
|
24
|
+
return super unless respond_to_missing?(method_name)
|
|
25
|
+
|
|
26
|
+
if @target.respond_to?(:"\#{method_name}=")
|
|
27
|
+
@target.public_send(:"\#{method_name}=", #{arg_forwarding})
|
|
28
|
+
else
|
|
29
|
+
@target.public_send(method_name, #{arg_forwarding})
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
RUBY
|
|
35
33
|
|
|
36
34
|
# Tells whether or not a given element exists on the target
|
|
37
35
|
# @param method_name [Symbol] name of the missing method
|
|
38
36
|
# @param include_private [Boolean] should we include private in the check as well
|
|
39
37
|
def respond_to_missing?(method_name, include_private = false)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
@target.respond_to?(:"#{method_name}=", include_private) ||
|
|
39
|
+
@target.respond_to?(method_name, include_private) ||
|
|
40
|
+
super
|
|
43
41
|
end
|
|
44
42
|
end
|
|
45
43
|
end
|
|
@@ -14,16 +14,41 @@ module Karafka
|
|
|
14
14
|
# @raise [Karafka::Topic::NonMatchingTopicError] raised if topic name does not match
|
|
15
15
|
# any route defined by user using routes.draw
|
|
16
16
|
def find(topic_id)
|
|
17
|
+
find_by(id: topic_id) || raise(Errors::NonMatchingRouteError, topic_id)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Finds first reference of a given topic based on provided lookup attribute
|
|
21
|
+
# @param lookup [Hash<Symbol, String>] hash with attribute - value key pairs
|
|
22
|
+
# @return [Karafka::Routing::Topic, nil] proper route details or nil if not found
|
|
23
|
+
def find_by(lookup)
|
|
17
24
|
App.consumer_groups.each do |consumer_group|
|
|
18
25
|
consumer_group.topics.each do |topic|
|
|
19
|
-
return topic if
|
|
26
|
+
return topic if lookup.all? do |attribute, value|
|
|
27
|
+
topic.public_send(attribute) == value
|
|
28
|
+
end
|
|
20
29
|
end
|
|
21
30
|
end
|
|
22
31
|
|
|
23
|
-
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Finds the topic by name (in any consumer group) and if not present, will built a new
|
|
36
|
+
# representation of the topic with the defaults and default deserializer.
|
|
37
|
+
#
|
|
38
|
+
# This is used in places where we may operate on topics that are not part of the routing
|
|
39
|
+
# but we want to do something on them (display data, iterate over, etc)
|
|
40
|
+
# @param name [String] name of the topic we are looking for
|
|
41
|
+
# @return [Karafka::Routing::Topic]
|
|
42
|
+
#
|
|
43
|
+
# @note Please note, that in case of a new topic, it will have a newly built consumer group
|
|
44
|
+
# as well, that is not part of the routing.
|
|
45
|
+
def find_or_initialize_by_name(name)
|
|
46
|
+
find_by(name: name) || Topic.new(name, ConsumerGroup.new(name))
|
|
24
47
|
end
|
|
25
48
|
|
|
26
49
|
module_function :find
|
|
50
|
+
module_function :find_by
|
|
51
|
+
module_function :find_or_initialize_by_name
|
|
27
52
|
end
|
|
28
53
|
end
|
|
29
54
|
end
|
|
@@ -8,13 +8,36 @@ module Karafka
|
|
|
8
8
|
# @note One subscription group will always belong to one consumer group, but one consumer
|
|
9
9
|
# group can have multiple subscription groups.
|
|
10
10
|
class SubscriptionGroup
|
|
11
|
-
attr_reader :id, :topics
|
|
11
|
+
attr_reader :id, :name, :topics, :kafka, :consumer_group
|
|
12
12
|
|
|
13
|
+
# Numeric for counting groups
|
|
14
|
+
GROUP_COUNT = Concurrent::AtomicFixnum.new
|
|
15
|
+
|
|
16
|
+
private_constant :GROUP_COUNT
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
# Generates new subscription group id that will be used in case of anonymous subscription
|
|
20
|
+
# groups
|
|
21
|
+
# @return [String] hex(6) compatible reproducible id
|
|
22
|
+
def id
|
|
23
|
+
::Digest::MD5.hexdigest(
|
|
24
|
+
GROUP_COUNT.increment.to_s
|
|
25
|
+
)[0..11]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @param position [Integer] position of this subscription group in all the subscriptions
|
|
30
|
+
# groups array. We need to have this value for sake of static group memberships, where
|
|
31
|
+
# we need a "in-between" restarts unique identifier
|
|
13
32
|
# @param topics [Karafka::Routing::Topics] all the topics that share the same key settings
|
|
14
33
|
# @return [SubscriptionGroup] built subscription group
|
|
15
|
-
def initialize(topics)
|
|
16
|
-
@
|
|
34
|
+
def initialize(position, topics)
|
|
35
|
+
@name = topics.first.subscription_group
|
|
36
|
+
@consumer_group = topics.first.consumer_group
|
|
37
|
+
@id = "#{@name}_#{position}"
|
|
38
|
+
@position = position
|
|
17
39
|
@topics = topics
|
|
40
|
+
@kafka = build_kafka
|
|
18
41
|
freeze
|
|
19
42
|
end
|
|
20
43
|
|
|
@@ -33,12 +56,27 @@ module Karafka
|
|
|
33
56
|
@topics.first.max_wait_time
|
|
34
57
|
end
|
|
35
58
|
|
|
59
|
+
# @return [Boolean] is this subscription group one of active once
|
|
60
|
+
def active?
|
|
61
|
+
Karafka::App.config.internal.routing.activity_manager.active?(:subscription_groups, name)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
36
66
|
# @return [Hash] kafka settings are a bit special. They are exactly the same for all of the
|
|
37
67
|
# topics but they lack the group.id (unless explicitly) provided. To make it compatible
|
|
38
68
|
# with our routing engine, we inject it before it will go to the consumer
|
|
39
|
-
def
|
|
40
|
-
kafka = @topics.first.kafka.dup
|
|
69
|
+
def build_kafka
|
|
70
|
+
kafka = Setup::AttributesMap.consumer(@topics.first.kafka.dup)
|
|
71
|
+
|
|
72
|
+
# If we use static group memberships, there can be a case, where same instance id would
|
|
73
|
+
# be set on many subscription groups as the group instance id from Karafka perspective is
|
|
74
|
+
# set per config. Each instance even if they are subscribed to different topics needs to
|
|
75
|
+
# have if fully unique. To make sure of that, we just add extra postfix at the end that
|
|
76
|
+
# increments.
|
|
77
|
+
group_instance_id = kafka.fetch(:'group.instance.id', false)
|
|
41
78
|
|
|
79
|
+
kafka[:'group.instance.id'] = "#{group_instance_id}_#{@position}" if group_instance_id
|
|
42
80
|
kafka[:'client.id'] ||= Karafka::App.config.client_id
|
|
43
81
|
kafka[:'group.id'] ||= @topics.first.consumer_group.id
|
|
44
82
|
kafka[:'auto.offset.reset'] ||= @topics.first.initial_offset
|
|
@@ -19,10 +19,15 @@ module Karafka
|
|
|
19
19
|
max_messages
|
|
20
20
|
max_wait_time
|
|
21
21
|
initial_offset
|
|
22
|
+
subscription_group
|
|
22
23
|
].freeze
|
|
23
24
|
|
|
24
25
|
private_constant :DISTRIBUTION_KEYS
|
|
25
26
|
|
|
27
|
+
def initialize
|
|
28
|
+
@position = -1
|
|
29
|
+
end
|
|
30
|
+
|
|
26
31
|
# @param topics [Karafka::Routing::Topics] all the topics based on which we want to build
|
|
27
32
|
# subscription groups
|
|
28
33
|
# @return [Array<SubscriptionGroup>] all subscription groups we need in separate threads
|
|
@@ -33,7 +38,7 @@ module Karafka
|
|
|
33
38
|
.values
|
|
34
39
|
.map { |value| value.map(&:last) }
|
|
35
40
|
.map { |topics_array| Routing::Topics.new(topics_array) }
|
|
36
|
-
.map { |grouped_topics| SubscriptionGroup.new(grouped_topics) }
|
|
41
|
+
.map { |grouped_topics| SubscriptionGroup.new(@position += 1, grouped_topics) }
|
|
37
42
|
end
|
|
38
43
|
|
|
39
44
|
private
|
|
@@ -8,12 +8,12 @@ module Karafka
|
|
|
8
8
|
class Topic
|
|
9
9
|
attr_reader :id, :name, :consumer_group
|
|
10
10
|
attr_writer :consumer
|
|
11
|
+
attr_accessor :subscription_group
|
|
11
12
|
|
|
12
13
|
# Attributes we can inherit from the root unless they were defined on this level
|
|
13
14
|
INHERITABLE_ATTRIBUTES = %i[
|
|
14
15
|
kafka
|
|
15
16
|
deserializer
|
|
16
|
-
manual_offset_management
|
|
17
17
|
max_messages
|
|
18
18
|
max_wait_time
|
|
19
19
|
initial_offset
|
|
@@ -27,6 +27,7 @@ module Karafka
|
|
|
27
27
|
@name = name.to_s
|
|
28
28
|
@consumer_group = consumer_group
|
|
29
29
|
@attributes = {}
|
|
30
|
+
@active = true
|
|
30
31
|
# @note We use identifier related to the consumer group that owns a topic, because from
|
|
31
32
|
# Karafka 0.6 we can handle multiple Kafka instances with the same process and we can
|
|
32
33
|
# have same topic name across multiple consumer groups
|
|
@@ -66,6 +67,12 @@ module Karafka
|
|
|
66
67
|
end
|
|
67
68
|
end
|
|
68
69
|
|
|
70
|
+
# Allows to disable topic by invoking this method and setting it to `false`.
|
|
71
|
+
# @param active [Boolean] should this topic be consumed or not
|
|
72
|
+
def active(active)
|
|
73
|
+
@active = active
|
|
74
|
+
end
|
|
75
|
+
|
|
69
76
|
# @return [Class] consumer class that we should use
|
|
70
77
|
# @note This is just an alias to the `#consumer` method. We however want to use it internally
|
|
71
78
|
# instead of referencing the `#consumer`. We use this to indicate that this method returns
|
|
@@ -75,9 +82,12 @@ module Karafka
|
|
|
75
82
|
consumer
|
|
76
83
|
end
|
|
77
84
|
|
|
78
|
-
# @return [Boolean]
|
|
79
|
-
def
|
|
80
|
-
|
|
85
|
+
# @return [Boolean] should this topic be in use
|
|
86
|
+
def active?
|
|
87
|
+
# Never active if disabled via routing
|
|
88
|
+
return false unless @active
|
|
89
|
+
|
|
90
|
+
Karafka::App.config.internal.routing.activity_manager.active?(:topics, name)
|
|
81
91
|
end
|
|
82
92
|
|
|
83
93
|
# @return [Hash] hash with all the topic attributes
|
|
@@ -90,8 +100,10 @@ module Karafka
|
|
|
90
100
|
Hash[map].merge!(
|
|
91
101
|
id: id,
|
|
92
102
|
name: name,
|
|
103
|
+
active: active?,
|
|
93
104
|
consumer: consumer,
|
|
94
|
-
consumer_group_id: consumer_group.id
|
|
105
|
+
consumer_group_id: consumer_group.id,
|
|
106
|
+
subscription_group: subscription_group
|
|
95
107
|
).freeze
|
|
96
108
|
end
|
|
97
109
|
end
|
|
@@ -23,6 +23,14 @@ module Karafka
|
|
|
23
23
|
@accumulator.each(&block)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
# Allows us to remove elements from the topics
|
|
27
|
+
#
|
|
28
|
+
# Block to decide what to delete
|
|
29
|
+
# @param block [Proc]
|
|
30
|
+
def delete_if(&block)
|
|
31
|
+
@accumulator.delete_if(&block)
|
|
32
|
+
end
|
|
33
|
+
|
|
26
34
|
# Finds topic by its name
|
|
27
35
|
#
|
|
28
36
|
# @param topic_name [String] topic name
|
data/lib/karafka/runner.rb
CHANGED
|
@@ -19,6 +19,7 @@ module Karafka
|
|
|
19
19
|
# We aggregate threads here for a supervised shutdown process
|
|
20
20
|
Karafka::Server.workers = workers
|
|
21
21
|
Karafka::Server.listeners = listeners
|
|
22
|
+
Karafka::Server.jobs_queue = jobs_queue
|
|
22
23
|
|
|
23
24
|
# All the listener threads need to finish
|
|
24
25
|
listeners.each(&:join)
|
|
@@ -10,6 +10,7 @@ module Karafka
|
|
|
10
10
|
# @param message [Karafka::Messages::Message] Message object that we want to deserialize
|
|
11
11
|
# @return [Hash] hash with deserialized JSON data
|
|
12
12
|
def call(message)
|
|
13
|
+
# nil payload can be present for example for tombstone messages
|
|
13
14
|
message.raw_payload.nil? ? nil : ::JSON.parse(message.raw_payload)
|
|
14
15
|
end
|
|
15
16
|
end
|
data/lib/karafka/server.rb
CHANGED
|
@@ -20,17 +20,27 @@ module Karafka
|
|
|
20
20
|
# Set of workers
|
|
21
21
|
attr_accessor :workers
|
|
22
22
|
|
|
23
|
-
#
|
|
24
|
-
|
|
23
|
+
# Jobs queue
|
|
24
|
+
attr_accessor :jobs_queue
|
|
25
25
|
|
|
26
26
|
# Method which runs app
|
|
27
27
|
def run
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
self.listeners = []
|
|
29
|
+
self.workers = []
|
|
30
|
+
|
|
31
|
+
# We need to validate this prior to running because it may be executed also from the
|
|
32
|
+
# embedded
|
|
33
|
+
# We cannot validate this during the start because config needs to be populated and routes
|
|
34
|
+
# need to be defined.
|
|
35
|
+
Contracts::ServerCliOptions.new.validate!(
|
|
36
|
+
Karafka::App.config.internal.routing.activity_manager.to_h
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
process.on_sigint { stop }
|
|
40
|
+
process.on_sigquit { stop }
|
|
41
|
+
process.on_sigterm { stop }
|
|
42
|
+
process.on_sigtstp { quiet }
|
|
43
|
+
process.supervise
|
|
34
44
|
|
|
35
45
|
# Start is blocking until stop is called and when we stop, it will wait until
|
|
36
46
|
# all of the things are ready to stop
|
|
@@ -38,9 +48,9 @@ module Karafka
|
|
|
38
48
|
|
|
39
49
|
# We always need to wait for Karafka to stop here since we should wait for the stop running
|
|
40
50
|
# in a separate thread (or trap context) to indicate everything is closed
|
|
41
|
-
# Since `#start` is blocking, we
|
|
51
|
+
# Since `#start` is blocking, we will get here only after the runner is done. This will
|
|
42
52
|
# not add any performance degradation because of that.
|
|
43
|
-
|
|
53
|
+
sleep(0.1) until Karafka::App.terminated?
|
|
44
54
|
# Try its best to shutdown underlying components before re-raising
|
|
45
55
|
# rubocop:disable Lint/RescueException
|
|
46
56
|
rescue Exception => e
|
|
@@ -50,18 +60,10 @@ module Karafka
|
|
|
50
60
|
raise e
|
|
51
61
|
end
|
|
52
62
|
|
|
53
|
-
# @return [Array<String>] array with names of consumer groups that should be consumed in a
|
|
54
|
-
# current server context
|
|
55
|
-
def consumer_groups
|
|
56
|
-
# If not specified, a server will listen on all the topics
|
|
57
|
-
@consumer_groups ||= Karafka::App.consumer_groups.map(&:name).freeze
|
|
58
|
-
end
|
|
59
|
-
|
|
60
63
|
# Starts Karafka with a supervision
|
|
61
64
|
# @note We don't need to sleep because Karafka::Fetcher is locking and waiting to
|
|
62
65
|
# finish loop (and it won't happen until we explicitly want to stop)
|
|
63
66
|
def start
|
|
64
|
-
process.supervise
|
|
65
67
|
Karafka::App.run!
|
|
66
68
|
Karafka::Runner.new.call
|
|
67
69
|
end
|
|
@@ -73,6 +75,11 @@ module Karafka
|
|
|
73
75
|
# lock them forever. If you need to run Karafka shutdown from within workers threads,
|
|
74
76
|
# please start a separate thread to do so.
|
|
75
77
|
def stop
|
|
78
|
+
# Initialize the stopping process only if Karafka was running
|
|
79
|
+
return if Karafka::App.stopping?
|
|
80
|
+
return if Karafka::App.stopped?
|
|
81
|
+
return if Karafka::App.terminated?
|
|
82
|
+
|
|
76
83
|
Karafka::App.stop!
|
|
77
84
|
|
|
78
85
|
timeout = Karafka::App.config.shutdown_timeout
|
|
@@ -81,13 +88,7 @@ module Karafka
|
|
|
81
88
|
# their work and if so, we can just return and normal shutdown process will take place
|
|
82
89
|
# We divide it by 1000 because we use time in ms.
|
|
83
90
|
((timeout / 1_000) * SUPERVISION_CHECK_FACTOR).to_i.times do
|
|
84
|
-
if listeners.count(&:alive?).zero? &&
|
|
85
|
-
workers.count(&:alive?).zero?
|
|
86
|
-
|
|
87
|
-
Karafka::App.producer.close
|
|
88
|
-
|
|
89
|
-
return
|
|
90
|
-
end
|
|
91
|
+
return if listeners.count(&:alive?).zero? && workers.count(&:alive?).zero?
|
|
91
92
|
|
|
92
93
|
sleep SUPERVISION_SLEEP
|
|
93
94
|
end
|
|
@@ -108,12 +109,41 @@ module Karafka
|
|
|
108
109
|
# This can cause memory leaks and crashes.
|
|
109
110
|
listeners.each(&:shutdown)
|
|
110
111
|
|
|
111
|
-
|
|
112
|
+
# We also do not forcefully terminate everything when running in the embedded mode,
|
|
113
|
+
# otherwise we would overwrite the shutdown process of the process that started Karafka
|
|
114
|
+
return unless process.supervised?
|
|
112
115
|
|
|
113
116
|
# exit! is not within the instrumentation as it would not trigger due to exit
|
|
114
|
-
Kernel.exit!
|
|
117
|
+
Kernel.exit!(FORCEFUL_EXIT_CODE)
|
|
115
118
|
ensure
|
|
116
|
-
|
|
119
|
+
# We need to check if it wasn't an early exit to make sure that only on stop invocation
|
|
120
|
+
# can change the status after everything is closed
|
|
121
|
+
if timeout
|
|
122
|
+
Karafka::App.stopped!
|
|
123
|
+
|
|
124
|
+
# We close producer as the last thing as it can be used in the notification pipeline
|
|
125
|
+
# to dispatch state changes, etc
|
|
126
|
+
Karafka::App.producer.close
|
|
127
|
+
|
|
128
|
+
Karafka::App.terminate!
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Quiets the Karafka server.
|
|
133
|
+
#
|
|
134
|
+
# Karafka will stop processing but won't quit the consumer group, so no rebalance will be
|
|
135
|
+
# triggered until final shutdown.
|
|
136
|
+
def quiet
|
|
137
|
+
# We don't have to safe-guard it with check states as the state transitions work only
|
|
138
|
+
# in one direction
|
|
139
|
+
Karafka::App.quiet!
|
|
140
|
+
|
|
141
|
+
# We need one more thread to monitor the process and move to quieted once everything
|
|
142
|
+
# is quiet and no processing is happening anymore
|
|
143
|
+
Thread.new do
|
|
144
|
+
sleep(0.1) until listeners.coordinators.all?(&:finished?)
|
|
145
|
+
Karafka::App.quieted!
|
|
146
|
+
end
|
|
117
147
|
end
|
|
118
148
|
|
|
119
149
|
private
|