karafka 2.5.0.rc2 → 2.5.1.beta1
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
- data/.github/workflows/{ci.yml → ci_linux_ubuntu_x86_64_gnu.yml} +54 -30
- data/.github/workflows/ci_macos_arm64.yml +148 -0
- data/.github/workflows/push.yml +2 -2
- data/.github/workflows/trigger-wiki-refresh.yml +30 -0
- data/.github/workflows/verify-action-pins.yml +1 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +29 -2
- data/Gemfile +2 -1
- data/Gemfile.lock +56 -27
- data/README.md +2 -2
- data/bin/integrations +3 -1
- data/bin/verify_kafka_warnings +2 -1
- data/config/locales/errors.yml +153 -152
- data/config/locales/pro_errors.yml +135 -134
- data/karafka.gemspec +3 -3
- data/lib/active_job/queue_adapters/karafka_adapter.rb +30 -1
- data/lib/karafka/active_job/dispatcher.rb +19 -9
- data/lib/karafka/admin/acl.rb +7 -8
- data/lib/karafka/admin/configs/config.rb +2 -2
- data/lib/karafka/admin/configs/resource.rb +2 -2
- data/lib/karafka/admin/configs.rb +3 -7
- data/lib/karafka/admin/consumer_groups.rb +351 -0
- data/lib/karafka/admin/topics.rb +206 -0
- data/lib/karafka/admin.rb +42 -451
- data/lib/karafka/base_consumer.rb +22 -0
- data/lib/karafka/{pro/contracts/server_cli_options.rb → cli/contracts/server.rb} +4 -12
- data/lib/karafka/cli/info.rb +1 -1
- data/lib/karafka/cli/install.rb +0 -2
- data/lib/karafka/connection/client.rb +8 -0
- data/lib/karafka/connection/listener.rb +5 -1
- data/lib/karafka/connection/status.rb +12 -9
- data/lib/karafka/errors.rb +0 -8
- data/lib/karafka/instrumentation/assignments_tracker.rb +16 -0
- data/lib/karafka/instrumentation/logger_listener.rb +109 -50
- data/lib/karafka/pro/active_job/dispatcher.rb +5 -0
- data/lib/karafka/pro/cleaner/messages/messages.rb +18 -8
- data/lib/karafka/pro/cli/contracts/server.rb +106 -0
- data/lib/karafka/pro/encryption/contracts/config.rb +1 -1
- data/lib/karafka/pro/loader.rb +1 -1
- data/lib/karafka/pro/recurring_tasks/contracts/config.rb +1 -1
- data/lib/karafka/pro/routing/features/adaptive_iterator/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/adaptive_iterator/topic.rb +9 -0
- data/lib/karafka/pro/routing/features/dead_letter_queue/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/dead_letter_queue/topic.rb +9 -0
- data/lib/karafka/pro/routing/features/delaying/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/delaying/topic.rb +9 -0
- data/lib/karafka/pro/routing/features/direct_assignments/contracts/consumer_group.rb +1 -1
- data/lib/karafka/pro/routing/features/direct_assignments/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/direct_assignments/topic.rb +9 -0
- data/lib/karafka/pro/routing/features/expiring/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/expiring/topic.rb +9 -0
- data/lib/karafka/pro/routing/features/filtering/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/filtering/topic.rb +9 -0
- data/lib/karafka/pro/routing/features/inline_insights/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/inline_insights/topic.rb +9 -0
- data/lib/karafka/pro/routing/features/long_running_job/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/long_running_job/topic.rb +9 -0
- data/lib/karafka/pro/routing/features/multiplexing/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/multiplexing.rb +1 -1
- data/lib/karafka/pro/routing/features/offset_metadata/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/offset_metadata/topic.rb +9 -0
- data/lib/karafka/pro/routing/features/parallel_segments/contracts/consumer_group.rb +1 -1
- data/lib/karafka/pro/routing/features/patterns/contracts/consumer_group.rb +1 -1
- data/lib/karafka/pro/routing/features/patterns/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/patterns/topic.rb +9 -0
- data/lib/karafka/pro/routing/features/pausing/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/periodic_job/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/periodic_job/topic.rb +9 -0
- data/lib/karafka/pro/routing/features/recurring_tasks/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/recurring_tasks/topic.rb +9 -0
- data/lib/karafka/pro/routing/features/scheduled_messages/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/scheduled_messages/topic.rb +9 -0
- data/lib/karafka/pro/routing/features/swarm/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/swarm/topic.rb +9 -0
- data/lib/karafka/pro/routing/features/throttling/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/throttling/topic.rb +9 -0
- data/lib/karafka/pro/routing/features/virtual_partitions/contracts/topic.rb +1 -1
- data/lib/karafka/pro/routing/features/virtual_partitions/topic.rb +9 -0
- data/lib/karafka/pro/scheduled_messages/contracts/config.rb +1 -1
- data/lib/karafka/pro/scheduled_messages/daily_buffer.rb +9 -3
- data/lib/karafka/pro/swarm/liveness_listener.rb +17 -2
- data/lib/karafka/processing/executor.rb +1 -1
- data/lib/karafka/routing/builder.rb +0 -3
- data/lib/karafka/routing/consumer_group.rb +1 -4
- data/lib/karafka/routing/contracts/consumer_group.rb +84 -0
- data/lib/karafka/routing/contracts/routing.rb +61 -0
- data/lib/karafka/routing/contracts/topic.rb +83 -0
- data/lib/karafka/routing/features/active_job/contracts/topic.rb +1 -1
- data/lib/karafka/routing/features/active_job/topic.rb +9 -0
- data/lib/karafka/routing/features/dead_letter_queue/contracts/topic.rb +1 -1
- data/lib/karafka/routing/features/dead_letter_queue/topic.rb +9 -0
- data/lib/karafka/routing/features/declaratives/contracts/topic.rb +1 -1
- data/lib/karafka/routing/features/declaratives/topic.rb +9 -0
- data/lib/karafka/routing/features/deserializers/contracts/topic.rb +1 -1
- data/lib/karafka/routing/features/deserializers/topic.rb +9 -0
- data/lib/karafka/routing/features/eofed/contracts/topic.rb +1 -1
- data/lib/karafka/routing/features/eofed/topic.rb +9 -0
- data/lib/karafka/routing/features/inline_insights/contracts/topic.rb +1 -1
- data/lib/karafka/routing/features/inline_insights/topic.rb +9 -0
- data/lib/karafka/routing/features/manual_offset_management/contracts/topic.rb +1 -1
- data/lib/karafka/routing/features/manual_offset_management/topic.rb +9 -0
- data/lib/karafka/routing/subscription_group.rb +1 -10
- data/lib/karafka/routing/topic.rb +9 -1
- data/lib/karafka/server.rb +2 -7
- data/lib/karafka/setup/attributes_map.rb +36 -0
- data/lib/karafka/setup/config.rb +6 -7
- data/lib/karafka/setup/contracts/config.rb +217 -0
- data/lib/karafka/setup/defaults_injector.rb +3 -1
- data/lib/karafka/swarm/node.rb +66 -6
- data/lib/karafka/swarm.rb +2 -2
- data/lib/karafka/templates/karafka.rb.erb +2 -7
- data/lib/karafka/version.rb +1 -1
- data/lib/karafka.rb +17 -18
- metadata +18 -15
- data/lib/karafka/contracts/config.rb +0 -210
- data/lib/karafka/contracts/consumer_group.rb +0 -81
- data/lib/karafka/contracts/routing.rb +0 -59
- data/lib/karafka/contracts/server_cli_options.rb +0 -92
- data/lib/karafka/contracts/topic.rb +0 -81
- data/lib/karafka/swarm/pidfd.rb +0 -147
@@ -10,6 +10,15 @@ module Karafka
|
|
10
10
|
class Expiring < Base
|
11
11
|
# Topic expiring API extensions
|
12
12
|
module Topic
|
13
|
+
# This method calls the parent class initializer and then sets up the
|
14
|
+
# extra instance variable to nil. The explicit initialization
|
15
|
+
# to nil is included as an optimization for Ruby's object shapes system,
|
16
|
+
# which improves memory layout and access performance.
|
17
|
+
def initialize(...)
|
18
|
+
super
|
19
|
+
@expiring = nil
|
20
|
+
end
|
21
|
+
|
13
22
|
# @param ttl [Integer, nil] maximum time in ms a message is considered alive
|
14
23
|
def expiring(ttl = nil)
|
15
24
|
# Those settings are used for validation
|
@@ -17,7 +17,7 @@ module Karafka
|
|
17
17
|
File.read(
|
18
18
|
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
19
19
|
)
|
20
|
-
).fetch('en').fetch('validations').fetch('topic')
|
20
|
+
).fetch('en').fetch('validations').fetch('routing').fetch('topic')
|
21
21
|
end
|
22
22
|
|
23
23
|
nested(:filtering) do
|
@@ -10,6 +10,15 @@ module Karafka
|
|
10
10
|
class Filtering < Base
|
11
11
|
# Filtering feature topic extensions
|
12
12
|
module Topic
|
13
|
+
# This method calls the parent class initializer and then sets up the
|
14
|
+
# extra instance variable to nil. The explicit initialization
|
15
|
+
# to nil is included as an optimization for Ruby's object shapes system,
|
16
|
+
# which improves memory layout and access performance.
|
17
|
+
def initialize(...)
|
18
|
+
super
|
19
|
+
@filtering = nil
|
20
|
+
end
|
21
|
+
|
13
22
|
# @param factory [#call, nil] Callable that can produce new filters instances per
|
14
23
|
# assigned topic partition. nil as default so this feature is disabled
|
15
24
|
def filter(factory = nil)
|
@@ -17,7 +17,7 @@ module Karafka
|
|
17
17
|
File.read(
|
18
18
|
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
19
19
|
)
|
20
|
-
).fetch('en').fetch('validations').fetch('topic')
|
20
|
+
).fetch('en').fetch('validations').fetch('routing').fetch('topic')
|
21
21
|
end
|
22
22
|
|
23
23
|
nested :inline_insights do
|
@@ -10,6 +10,15 @@ module Karafka
|
|
10
10
|
class InlineInsights < Base
|
11
11
|
# Routing topic inline insights API
|
12
12
|
module Topic
|
13
|
+
# This method calls the parent class initializer and then sets up the
|
14
|
+
# extra instance variable to nil. The explicit initialization
|
15
|
+
# to nil is included as an optimization for Ruby's object shapes system,
|
16
|
+
# which improves memory layout and access performance.
|
17
|
+
def initialize(...)
|
18
|
+
super
|
19
|
+
@inline_insights = nil
|
20
|
+
end
|
21
|
+
|
13
22
|
# @param active [Boolean] should inline insights be activated
|
14
23
|
# @param required [Boolean] are the insights required to operate
|
15
24
|
def inline_insights(active = -1, required: -1)
|
@@ -17,7 +17,7 @@ module Karafka
|
|
17
17
|
File.read(
|
18
18
|
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
19
19
|
)
|
20
|
-
).fetch('en').fetch('validations').fetch('topic')
|
20
|
+
).fetch('en').fetch('validations').fetch('routing').fetch('topic')
|
21
21
|
end
|
22
22
|
|
23
23
|
nested(:long_running_job) do
|
@@ -10,6 +10,15 @@ module Karafka
|
|
10
10
|
class LongRunningJob < Base
|
11
11
|
# Long-Running Jobs topic API extensions
|
12
12
|
module Topic
|
13
|
+
# This method calls the parent class initializer and then sets up the
|
14
|
+
# extra instance variable to nil. The explicit initialization
|
15
|
+
# to nil is included as an optimization for Ruby's object shapes system,
|
16
|
+
# which improves memory layout and access performance.
|
17
|
+
def initialize(...)
|
18
|
+
super
|
19
|
+
@long_running_job = nil
|
20
|
+
end
|
21
|
+
|
13
22
|
# @param active [Boolean] do we want to enable long-running job feature for this topic
|
14
23
|
def long_running_job(active = false)
|
15
24
|
@long_running_job ||= Config.new(active: active)
|
@@ -21,7 +21,7 @@ module Karafka
|
|
21
21
|
File.read(
|
22
22
|
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
23
23
|
)
|
24
|
-
).fetch('en').fetch('validations').fetch('topic')
|
24
|
+
).fetch('en').fetch('validations').fetch('routing').fetch('topic')
|
25
25
|
end
|
26
26
|
|
27
27
|
nested(:subscription_group_details) do
|
@@ -17,7 +17,7 @@ module Karafka
|
|
17
17
|
# @param _config [Karafka::Core::Configurable::Node] app config node
|
18
18
|
def pre_setup(_config)
|
19
19
|
# Make sure we use proper unique validator for topics definitions
|
20
|
-
::Karafka::Contracts::ConsumerGroup.singleton_class.prepend(
|
20
|
+
::Karafka::Routing::Contracts::ConsumerGroup.singleton_class.prepend(
|
21
21
|
Patches::Contracts::ConsumerGroup
|
22
22
|
)
|
23
23
|
end
|
@@ -17,7 +17,7 @@ module Karafka
|
|
17
17
|
File.read(
|
18
18
|
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
19
19
|
)
|
20
|
-
).fetch('en').fetch('validations').fetch('topic')
|
20
|
+
).fetch('en').fetch('validations').fetch('routing').fetch('topic')
|
21
21
|
end
|
22
22
|
|
23
23
|
nested(:offset_metadata) do
|
@@ -25,6 +25,15 @@ module Karafka
|
|
25
25
|
|
26
26
|
# Commit Metadata API extensions
|
27
27
|
module Topic
|
28
|
+
# This method calls the parent class initializer and then sets up the
|
29
|
+
# extra instance variable to nil. The explicit initialization
|
30
|
+
# to nil is included as an optimization for Ruby's object shapes system,
|
31
|
+
# which improves memory layout and access performance.
|
32
|
+
def initialize(...)
|
33
|
+
super
|
34
|
+
@offset_metadata = nil
|
35
|
+
end
|
36
|
+
|
28
37
|
# @param cache [Boolean] should we cache the response until rebalance
|
29
38
|
# @param deserializer [#call] deserializer that will get raw data and should return
|
30
39
|
# deserialized metadata
|
@@ -17,7 +17,7 @@ module Karafka
|
|
17
17
|
File.read(
|
18
18
|
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
19
19
|
)
|
20
|
-
).fetch('en').fetch('validations').fetch('consumer_group')
|
20
|
+
).fetch('en').fetch('validations').fetch('routing').fetch('consumer_group')
|
21
21
|
|
22
22
|
nested(:parallel_segments) do
|
23
23
|
required(:active) { |val| [true, false].include?(val) }
|
@@ -16,7 +16,7 @@ module Karafka
|
|
16
16
|
File.read(
|
17
17
|
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
18
18
|
)
|
19
|
-
).fetch('en').fetch('validations').fetch('consumer_group')
|
19
|
+
).fetch('en').fetch('validations').fetch('routing').fetch('consumer_group')
|
20
20
|
|
21
21
|
required(:patterns) { |val| val.is_a?(Array) && val.all? { |el| el.is_a?(Hash) } }
|
22
22
|
|
@@ -17,7 +17,7 @@ module Karafka
|
|
17
17
|
File.read(
|
18
18
|
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
19
19
|
)
|
20
|
-
).fetch('en').fetch('validations').fetch('topic')
|
20
|
+
).fetch('en').fetch('validations').fetch('routing').fetch('topic')
|
21
21
|
end
|
22
22
|
|
23
23
|
nested(:patterns) do
|
@@ -10,6 +10,15 @@ module Karafka
|
|
10
10
|
class Patterns < Base
|
11
11
|
# Patterns feature topic extensions
|
12
12
|
module Topic
|
13
|
+
# This method calls the parent class initializer and then sets up the
|
14
|
+
# extra instance variable to nil. The explicit initialization
|
15
|
+
# to nil is included as an optimization for Ruby's object shapes system,
|
16
|
+
# which improves memory layout and access performance.
|
17
|
+
def initialize(...)
|
18
|
+
super
|
19
|
+
@patterns = nil
|
20
|
+
end
|
21
|
+
|
13
22
|
# @return [String] subscription name or the regexp string representing matching of
|
14
23
|
# new topics that should be detected.
|
15
24
|
def subscription_name
|
@@ -17,7 +17,7 @@ module Karafka
|
|
17
17
|
File.read(
|
18
18
|
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
19
19
|
)
|
20
|
-
).fetch('en').fetch('validations').fetch('topic')
|
20
|
+
).fetch('en').fetch('validations').fetch('routing').fetch('topic')
|
21
21
|
|
22
22
|
required(:pause_timeout) { |val| val.is_a?(Integer) && val.positive? }
|
23
23
|
required(:pause_max_timeout) { |val| val.is_a?(Integer) && val.positive? }
|
@@ -17,7 +17,7 @@ module Karafka
|
|
17
17
|
File.read(
|
18
18
|
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
19
19
|
)
|
20
|
-
).fetch('en').fetch('validations').fetch('topic')
|
20
|
+
).fetch('en').fetch('validations').fetch('routing').fetch('topic')
|
21
21
|
end
|
22
22
|
|
23
23
|
nested(:periodic_job) do
|
@@ -10,6 +10,15 @@ module Karafka
|
|
10
10
|
class PeriodicJob < Base
|
11
11
|
# Periodic topic action flows extensions
|
12
12
|
module Topic
|
13
|
+
# This method calls the parent class initializer and then sets up the
|
14
|
+
# extra instance variable to nil. The explicit initialization
|
15
|
+
# to nil is included as an optimization for Ruby's object shapes system,
|
16
|
+
# which improves memory layout and access performance.
|
17
|
+
def initialize(...)
|
18
|
+
super
|
19
|
+
@periodic_job = nil
|
20
|
+
end
|
21
|
+
|
13
22
|
# Defines topic as periodic. Periodic topics consumers will invoke `#tick` with each
|
14
23
|
# poll where messages were not received.
|
15
24
|
# @param active [Boolean] should ticking happen for this topic assignments.
|
@@ -17,7 +17,7 @@ module Karafka
|
|
17
17
|
File.read(
|
18
18
|
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
19
19
|
)
|
20
|
-
).fetch('en').fetch('validations').fetch('topic')
|
20
|
+
).fetch('en').fetch('validations').fetch('routing').fetch('topic')
|
21
21
|
end
|
22
22
|
|
23
23
|
nested(:recurring_tasks) do
|
@@ -11,6 +11,15 @@ module Karafka
|
|
11
11
|
# Topic extensions to be able to check if given topic is a recurring tasks topic
|
12
12
|
# Please note, that this applies to both the schedules topics and reports topics
|
13
13
|
module Topic
|
14
|
+
# This method calls the parent class initializer and then sets up the
|
15
|
+
# extra instance variable to nil. The explicit initialization
|
16
|
+
# to nil is included as an optimization for Ruby's object shapes system,
|
17
|
+
# which improves memory layout and access performance.
|
18
|
+
def initialize(...)
|
19
|
+
super
|
20
|
+
@recurring_tasks = nil
|
21
|
+
end
|
22
|
+
|
14
23
|
# @param active [Boolean] should this topic be considered related to recurring tasks
|
15
24
|
def recurring_tasks(active = false)
|
16
25
|
@recurring_tasks ||= Config.new(active: active)
|
@@ -17,7 +17,7 @@ module Karafka
|
|
17
17
|
File.read(
|
18
18
|
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
19
19
|
)
|
20
|
-
).fetch('en').fetch('validations').fetch('topic')
|
20
|
+
).fetch('en').fetch('validations').fetch('routing').fetch('topic')
|
21
21
|
end
|
22
22
|
|
23
23
|
nested(:scheduled_messages) do
|
@@ -11,6 +11,15 @@ module Karafka
|
|
11
11
|
# Topic extensions to be able to check if given topic is a scheduled messages topic
|
12
12
|
# Please note, that this applies to both the schedules topic and logs topic
|
13
13
|
module Topic
|
14
|
+
# This method calls the parent class initializer and then sets up the
|
15
|
+
# extra instance variable to nil. The explicit initialization
|
16
|
+
# to nil is included as an optimization for Ruby's object shapes system,
|
17
|
+
# which improves memory layout and access performance.
|
18
|
+
def initialize(...)
|
19
|
+
super
|
20
|
+
@scheduled_messages = nil
|
21
|
+
end
|
22
|
+
|
14
23
|
# @param active [Boolean] should this topic be considered related to scheduled messages
|
15
24
|
def scheduled_messages(active = false)
|
16
25
|
@scheduled_messages ||= Config.new(active: active)
|
@@ -10,6 +10,15 @@ module Karafka
|
|
10
10
|
class Swarm < Base
|
11
11
|
# Topic swarm API extensions
|
12
12
|
module Topic
|
13
|
+
# This method calls the parent class initializer and then sets up the
|
14
|
+
# extra instance variable to nil. The explicit initialization
|
15
|
+
# to nil is included as an optimization for Ruby's object shapes system,
|
16
|
+
# which improves memory layout and access performance.
|
17
|
+
def initialize(...)
|
18
|
+
super
|
19
|
+
@swarm = nil
|
20
|
+
end
|
21
|
+
|
13
22
|
# Allows defining swarm routing topic settings
|
14
23
|
# @param nodes [Range, Array, Hash] range of nodes ids or array with nodes ids for
|
15
24
|
# which we should run given topic or hash with nodes expected partition assignments
|
@@ -17,7 +17,7 @@ module Karafka
|
|
17
17
|
File.read(
|
18
18
|
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
19
19
|
)
|
20
|
-
).fetch('en').fetch('validations').fetch('topic')
|
20
|
+
).fetch('en').fetch('validations').fetch('routing').fetch('topic')
|
21
21
|
end
|
22
22
|
|
23
23
|
nested(:throttling) do
|
@@ -10,6 +10,15 @@ module Karafka
|
|
10
10
|
class Throttling < Base
|
11
11
|
# Topic throttling API extensions
|
12
12
|
module Topic
|
13
|
+
# This method calls the parent class initializer and then sets up the
|
14
|
+
# extra instance variable to nil. The explicit initialization
|
15
|
+
# to nil is included as an optimization for Ruby's object shapes system,
|
16
|
+
# which improves memory layout and access performance.
|
17
|
+
def initialize(...)
|
18
|
+
super
|
19
|
+
@throttling = nil
|
20
|
+
end
|
21
|
+
|
13
22
|
# @param limit [Integer] max messages to process in an time interval
|
14
23
|
# @param interval [Integer] time interval for processing
|
15
24
|
def throttling(
|
@@ -17,7 +17,7 @@ module Karafka
|
|
17
17
|
File.read(
|
18
18
|
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
19
19
|
)
|
20
|
-
).fetch('en').fetch('validations').fetch('topic')
|
20
|
+
).fetch('en').fetch('validations').fetch('routing').fetch('topic')
|
21
21
|
end
|
22
22
|
|
23
23
|
nested(:virtual_partitions) do
|
@@ -10,6 +10,15 @@ module Karafka
|
|
10
10
|
class VirtualPartitions < Base
|
11
11
|
# Topic extensions to be able to manage virtual partitions feature
|
12
12
|
module Topic
|
13
|
+
# This method calls the parent class initializer and then sets up the
|
14
|
+
# extra instance variable to nil. The explicit initialization
|
15
|
+
# to nil is included as an optimization for Ruby's object shapes system,
|
16
|
+
# which improves memory layout and access performance.
|
17
|
+
def initialize(...)
|
18
|
+
super
|
19
|
+
@virtual_partitions = nil
|
20
|
+
end
|
21
|
+
|
13
22
|
# @param max_partitions [Integer] max number of virtual partitions that can come out of
|
14
23
|
# the single distribution flow. When set to more than the Karafka threading, will
|
15
24
|
# create more work than workers. When less, can ensure we have spare resources to
|
@@ -15,7 +15,7 @@ module Karafka
|
|
15
15
|
File.read(
|
16
16
|
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
17
17
|
)
|
18
|
-
).fetch('en').fetch('validations').fetch('config')
|
18
|
+
).fetch('en').fetch('validations').fetch('setup').fetch('config')
|
19
19
|
end
|
20
20
|
|
21
21
|
nested(:scheduled_messages) do
|
@@ -58,9 +58,15 @@ module Karafka
|
|
58
58
|
selected << [epoch, message]
|
59
59
|
end
|
60
60
|
|
61
|
-
|
62
|
-
|
63
|
-
|
61
|
+
# When epoch is of the same value for multiple messages to be dispatched, we also sort
|
62
|
+
# on the offset to make sure that earlier messages are dispatched prior to newer
|
63
|
+
selected.sort! do |pck1, pck2|
|
64
|
+
cmp = pck1[0] <=> pck2[0]
|
65
|
+
|
66
|
+
cmp.zero? ? pck1[1].offset <=> pck2[1].offset : cmp
|
67
|
+
end
|
68
|
+
|
69
|
+
selected.each { |_, message| yield(message) }
|
64
70
|
end
|
65
71
|
|
66
72
|
# Removes given key from the accumulator
|
@@ -163,19 +163,34 @@ module Karafka
|
|
163
163
|
end
|
164
164
|
|
165
165
|
# @return [Integer] RSS in MB for the current process
|
166
|
-
# @note Since swarm is linux only, we do not have to worry about getting RSS on other OSes
|
167
166
|
def rss_mb
|
167
|
+
RUBY_PLATFORM.include?('linux') ? rss_mb_linux : rss_mb_macos
|
168
|
+
end
|
169
|
+
|
170
|
+
# @return [Integer] RSS in MB for the current process on Linux
|
171
|
+
def rss_mb_linux
|
168
172
|
kb_rss = 0
|
169
173
|
|
170
174
|
IO.readlines("/proc/#{node.pid}/status").each do |line|
|
171
175
|
next unless line.start_with?('VmRSS:')
|
172
176
|
|
173
177
|
kb_rss = line.split[1].to_i
|
174
|
-
|
175
178
|
break
|
176
179
|
end
|
177
180
|
|
178
181
|
(kb_rss / 1_024.to_i).round
|
182
|
+
rescue Errno::ENOENT, Errno::EACCES
|
183
|
+
# /proc file doesn't exist or no permission to read it
|
184
|
+
0
|
185
|
+
end
|
186
|
+
|
187
|
+
# @return [Integer] RSS in MB for the current process on macOS/BSD
|
188
|
+
def rss_mb_macos
|
189
|
+
output = `ps -o rss= -p #{node.pid}`.strip
|
190
|
+
output.empty? ? 0 : (output.to_i / 1_024.0).round
|
191
|
+
rescue Errno::ESRCH
|
192
|
+
# Process doesn't exist
|
193
|
+
0
|
179
194
|
end
|
180
195
|
end
|
181
196
|
end
|
@@ -168,7 +168,6 @@ module Karafka
|
|
168
168
|
topic = @coordinator.topic
|
169
169
|
|
170
170
|
strategy = strategy_selector.find(topic)
|
171
|
-
expansions = expansions_selector.find(topic)
|
172
171
|
|
173
172
|
consumer = topic.consumer_class.new
|
174
173
|
# We use singleton class as the same consumer class may be used to process different
|
@@ -178,6 +177,7 @@ module Karafka
|
|
178
177
|
# Specific features may expand consumer API beyond the injected strategy. The difference
|
179
178
|
# here is that strategy impacts the flow of states while extra APIs just provide some
|
180
179
|
# extra methods with informations, etc but do no deviate the flow behavior
|
180
|
+
expansions = expansions_selector.find(topic)
|
181
181
|
expansions.each { |expansion| consumer.singleton_class.include(expansion) }
|
182
182
|
|
183
183
|
consumer.client = @client
|
@@ -157,9 +157,6 @@ module Karafka
|
|
157
157
|
end
|
158
158
|
end
|
159
159
|
|
160
|
-
# In case we use simple style of routing, all topics will be assigned to the same consumer
|
161
|
-
# group that will be based on the client_id
|
162
|
-
#
|
163
160
|
# @param topic_name [String, Symbol] name of a topic from which we want to consumer
|
164
161
|
# @param block [Proc] proc we want to evaluate in the topic context
|
165
162
|
def topic(topic_name, &block)
|
@@ -22,10 +22,7 @@ module Karafka
|
|
22
22
|
# groups
|
23
23
|
attr_accessor :current_subscription_group_details
|
24
24
|
|
25
|
-
# @param name [String, Symbol]
|
26
|
-
# yet have an application client_id namespace, this will be added here by default.
|
27
|
-
# We add it to make a multi-system development easier for people that don't use
|
28
|
-
# kafka and don't understand the concept of consumer groups.
|
25
|
+
# @param name [String, Symbol] name of this consumer group.
|
29
26
|
def initialize(name)
|
30
27
|
@name = name.to_s
|
31
28
|
# This used to be different when consumer mappers existed but now it is the same
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
module Routing
|
5
|
+
# Namespace for routing related contracts
|
6
|
+
module Contracts
|
7
|
+
# Contract for single full route (consumer group + topics) validation.
|
8
|
+
class ConsumerGroup < Karafka::Contracts::Base
|
9
|
+
configure do |config|
|
10
|
+
config.error_messages = YAML.safe_load(
|
11
|
+
File.read(
|
12
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
|
13
|
+
)
|
14
|
+
).fetch('en').fetch('validations').fetch('routing').fetch('consumer_group')
|
15
|
+
end
|
16
|
+
|
17
|
+
required(:id) { |val| val.is_a?(String) && Karafka::Contracts::TOPIC_REGEXP.match?(val) }
|
18
|
+
required(:topics) { |val| val.is_a?(Array) && !val.empty? }
|
19
|
+
|
20
|
+
virtual do |data, errors|
|
21
|
+
next unless errors.empty?
|
22
|
+
|
23
|
+
names = data.fetch(:topics).map { |topic| topic_unique_key(topic) }
|
24
|
+
|
25
|
+
next if names.size == names.uniq.size
|
26
|
+
|
27
|
+
[[%i[topics], :names_not_unique]]
|
28
|
+
end
|
29
|
+
|
30
|
+
# Prevent same topics subscriptions in one CG with different consumer classes
|
31
|
+
# This should prevent users from accidentally creating multi-sg one CG setup with weird
|
32
|
+
# different consumer usage. If you need to consume same topic twice, use distinct CGs.
|
33
|
+
virtual do |data, errors|
|
34
|
+
next unless errors.empty?
|
35
|
+
|
36
|
+
topics_consumers = Hash.new { |h, k| h[k] = Set.new }
|
37
|
+
|
38
|
+
data.fetch(:topics).map do |topic|
|
39
|
+
topics_consumers[topic[:name]] << topic[:consumer]
|
40
|
+
end
|
41
|
+
|
42
|
+
next if topics_consumers.values.map(&:size).all? { |count| count == 1 }
|
43
|
+
|
44
|
+
[[%i[topics], :many_consumers_same_topic]]
|
45
|
+
end
|
46
|
+
|
47
|
+
virtual do |data, errors|
|
48
|
+
next unless errors.empty?
|
49
|
+
next unless ::Karafka::App.config.strict_topics_namespacing
|
50
|
+
|
51
|
+
names = data.fetch(:topics).map { |topic| topic[:name] }
|
52
|
+
names_hash = names.each_with_object({}) { |n, h| h[n] = true }
|
53
|
+
error_occured = false
|
54
|
+
names.each do |n|
|
55
|
+
# Skip topic names that are not namespaced
|
56
|
+
next unless n.chars.find { |c| ['.', '_'].include?(c) }
|
57
|
+
|
58
|
+
if n.chars.include?('.')
|
59
|
+
# Check underscore styled topic
|
60
|
+
underscored_topic = n.tr('.', '_')
|
61
|
+
error_occured = names_hash[underscored_topic] ? true : false
|
62
|
+
else
|
63
|
+
# Check dot styled topic
|
64
|
+
dot_topic = n.tr('_', '.')
|
65
|
+
error_occured = names_hash[dot_topic] ? true : false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
next unless error_occured
|
70
|
+
|
71
|
+
[[%i[topics], :topics_namespaced_names_not_unique]]
|
72
|
+
end
|
73
|
+
|
74
|
+
class << self
|
75
|
+
# @param topic [Hash] topic config hash
|
76
|
+
# @return [String] topic unique key for validators
|
77
|
+
def topic_unique_key(topic)
|
78
|
+
topic[:name]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
module Routing
|
5
|
+
module Contracts
|
6
|
+
# Ensures that routing wide rules are obeyed
|
7
|
+
class Routing < Karafka::Contracts::Base
|
8
|
+
configure do |config|
|
9
|
+
config.error_messages = YAML.safe_load(
|
10
|
+
File.read(
|
11
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
|
12
|
+
)
|
13
|
+
).fetch('en').fetch('validations').fetch('routing')
|
14
|
+
end
|
15
|
+
|
16
|
+
# Ensures, that when declarative topics strict requirement is on, all topics have
|
17
|
+
# declarative definition (including DLQ topics)
|
18
|
+
# @note It will ignore routing pattern topics because those topics are virtual
|
19
|
+
virtual do |data, errors|
|
20
|
+
next unless errors.empty?
|
21
|
+
# Do not validate declaratives unless required and explicitly enabled
|
22
|
+
next unless Karafka::App.config.strict_declarative_topics
|
23
|
+
|
24
|
+
# Collects declarative topics. Please note, that any topic that has a `#topic` reference,
|
25
|
+
# will be declarative by default unless explicitly disabled. This however does not apply
|
26
|
+
# to the DLQ definitions
|
27
|
+
dec_topics = Set.new
|
28
|
+
# All topics including the DLQ topics names that are marked as active
|
29
|
+
topics = Set.new
|
30
|
+
|
31
|
+
data.each do |consumer_group|
|
32
|
+
consumer_group[:topics].each do |topic|
|
33
|
+
pat = topic[:patterns]
|
34
|
+
# Ignore pattern topics because they won't exist and should not be declarative
|
35
|
+
# managed
|
36
|
+
topics << topic[:name] if !pat || !pat[:active]
|
37
|
+
|
38
|
+
dlq = topic[:dead_letter_queue]
|
39
|
+
topics << dlq[:topic] if dlq[:active]
|
40
|
+
|
41
|
+
dec = topic[:declaratives]
|
42
|
+
|
43
|
+
dec_topics << topic[:name] if dec[:active]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
missing_dec = topics - dec_topics
|
48
|
+
|
49
|
+
next if missing_dec.empty?
|
50
|
+
|
51
|
+
missing_dec.map do |topic_name|
|
52
|
+
[
|
53
|
+
[:topics, topic_name],
|
54
|
+
:without_declarative_definition
|
55
|
+
]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|