karafka 2.1.13 → 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +39 -0
- data/Gemfile.lock +4 -4
- data/config/locales/errors.yml +4 -0
- data/config/locales/pro_errors.yml +17 -0
- data/karafka.gemspec +1 -1
- data/lib/karafka/admin.rb +21 -33
- data/lib/karafka/connection/client.rb +1 -1
- data/lib/karafka/contracts/config.rb +24 -0
- data/lib/karafka/pro/contracts/base.rb +23 -0
- data/lib/karafka/pro/contracts/server_cli_options.rb +111 -0
- data/lib/karafka/pro/loader.rb +4 -1
- data/lib/karafka/pro/routing/features/active_job/builder.rb +45 -0
- data/lib/karafka/pro/routing/features/active_job.rb +26 -0
- data/lib/karafka/pro/routing/features/dead_letter_queue/contracts/topic.rb +53 -0
- data/lib/karafka/pro/routing/features/delaying/contracts/topic.rb +41 -0
- data/lib/karafka/pro/routing/features/expiring/contracts/topic.rb +41 -0
- data/lib/karafka/pro/routing/features/filtering/contracts/topic.rb +44 -0
- data/lib/karafka/pro/routing/features/long_running_job/{contract.rb → contracts/topic.rb} +14 -11
- data/lib/karafka/pro/routing/features/{filtering/contract.rb → patterns/builder.rb} +13 -16
- data/lib/karafka/pro/routing/features/patterns/config.rb +54 -0
- data/lib/karafka/pro/routing/features/patterns/consumer_group.rb +68 -0
- data/lib/karafka/pro/routing/features/patterns/contracts/consumer_group.rb +62 -0
- data/lib/karafka/pro/routing/features/patterns/contracts/pattern.rb +46 -0
- data/lib/karafka/pro/routing/features/patterns/contracts/topic.rb +41 -0
- data/lib/karafka/pro/routing/features/patterns/detector.rb +68 -0
- data/lib/karafka/pro/routing/features/patterns/pattern.rb +95 -0
- data/lib/karafka/pro/routing/features/{delaying/contract.rb → patterns/patterns.rb} +11 -14
- data/lib/karafka/pro/routing/features/patterns/topic.rb +50 -0
- data/lib/karafka/pro/routing/features/patterns/topics.rb +53 -0
- data/lib/karafka/pro/routing/features/patterns.rb +33 -0
- data/lib/karafka/pro/routing/features/pausing/contracts/topic.rb +51 -0
- data/lib/karafka/pro/routing/features/throttling/contracts/topic.rb +44 -0
- data/lib/karafka/pro/routing/features/virtual_partitions/contracts/topic.rb +55 -0
- data/lib/karafka/routing/consumer_group.rb +1 -1
- data/lib/karafka/routing/features/active_job/contracts/topic.rb +44 -0
- data/lib/karafka/routing/features/active_job/proxy.rb +14 -0
- data/lib/karafka/routing/features/base/expander.rb +8 -2
- data/lib/karafka/routing/features/base.rb +4 -2
- data/lib/karafka/routing/features/dead_letter_queue/contracts/topic.rb +46 -0
- data/lib/karafka/routing/features/declaratives/contracts/topic.rb +33 -0
- data/lib/karafka/routing/features/manual_offset_management/contracts/topic.rb +27 -0
- data/lib/karafka/routing/router.rb +0 -11
- data/lib/karafka/routing/subscription_group.rb +9 -0
- data/lib/karafka/routing/topic.rb +5 -0
- data/lib/karafka/server.rb +9 -4
- data/lib/karafka/setup/config.rb +45 -0
- data/lib/karafka/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +36 -19
- metadata.gz.sig +0 -0
- data/lib/karafka/pro/routing/features/dead_letter_queue/contract.rb +0 -50
- data/lib/karafka/pro/routing/features/expiring/contract.rb +0 -38
- data/lib/karafka/pro/routing/features/pausing/contract.rb +0 -48
- data/lib/karafka/pro/routing/features/throttling/contract.rb +0 -41
- data/lib/karafka/pro/routing/features/virtual_partitions/contract.rb +0 -52
- data/lib/karafka/routing/features/active_job/contract.rb +0 -41
- data/lib/karafka/routing/features/dead_letter_queue/contract.rb +0 -42
- data/lib/karafka/routing/features/declaratives/contract.rb +0 -30
- data/lib/karafka/routing/features/manual_offset_management/contract.rb +0 -24
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module Routing
|
17
|
+
module Features
|
18
|
+
class Filtering < Base
|
19
|
+
# Namespace for filtering feature contracts
|
20
|
+
module Contracts
|
21
|
+
# Contract to validate configuration of the filtering feature
|
22
|
+
class Topic < Karafka::Contracts::Base
|
23
|
+
configure do |config|
|
24
|
+
config.error_messages = YAML.safe_load(
|
25
|
+
File.read(
|
26
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
27
|
+
)
|
28
|
+
).fetch('en').fetch('validations').fetch('topic')
|
29
|
+
end
|
30
|
+
|
31
|
+
nested(:filtering) do
|
32
|
+
required(:active) { |val| [true, false].include?(val) }
|
33
|
+
|
34
|
+
required(:factories) do |val|
|
35
|
+
val.is_a?(Array) && val.all? { |factory| factory.respond_to?(:call) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -16,18 +16,21 @@ module Karafka
|
|
16
16
|
module Routing
|
17
17
|
module Features
|
18
18
|
class LongRunningJob < Base
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
19
|
+
# Namespace for LRJ contracts
|
20
|
+
module Contracts
|
21
|
+
# Rules around long-running job settings
|
22
|
+
class Topic < Karafka::Contracts::Base
|
23
|
+
configure do |config|
|
24
|
+
config.error_messages = YAML.safe_load(
|
25
|
+
File.read(
|
26
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
27
|
+
)
|
28
|
+
).fetch('en').fetch('validations').fetch('topic')
|
29
|
+
end
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
+
nested(:long_running_job) do
|
32
|
+
required(:active) { |val| [true, false].include?(val) }
|
33
|
+
end
|
31
34
|
end
|
32
35
|
end
|
33
36
|
end
|
@@ -15,22 +15,19 @@ module Karafka
|
|
15
15
|
module Pro
|
16
16
|
module Routing
|
17
17
|
module Features
|
18
|
-
class
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
required(:factories) do |val|
|
33
|
-
val.is_a?(Array) && val.all? { |factory| factory.respond_to?(:call) }
|
18
|
+
class Patterns < Base
|
19
|
+
# Expansions for the routing builder
|
20
|
+
module Builder
|
21
|
+
# Allows us to define the simple routing pattern matching
|
22
|
+
#
|
23
|
+
# @param regexp_or_name [Symbol, String, Regexp] name of the pattern or regexp for
|
24
|
+
# automatic-based named patterns
|
25
|
+
# @param regexp [Regexp, nil] nil if we use auto-generated name based on the regexp or
|
26
|
+
# the regexp if we used named patterns
|
27
|
+
# @param block [Proc]
|
28
|
+
def pattern(regexp_or_name, regexp = nil, &block)
|
29
|
+
consumer_group('app') do
|
30
|
+
pattern(regexp_or_name, regexp, &block)
|
34
31
|
end
|
35
32
|
end
|
36
33
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module Routing
|
17
|
+
module Features
|
18
|
+
# Holds pattern info reference
|
19
|
+
# Type is set to:
|
20
|
+
# `:regular` - in case patterns are not used and topic is just a regular existing topic
|
21
|
+
# matched directly based on the name
|
22
|
+
# `:discovered` - in case it is a real topic on which we started to listed
|
23
|
+
# `:matcher` - represents a regular expression used by librdkafka
|
24
|
+
class Patterns < Base
|
25
|
+
# Config for pattern based topic
|
26
|
+
# Only pattern related topics are active in this context
|
27
|
+
Config = Struct.new(
|
28
|
+
:active,
|
29
|
+
:type,
|
30
|
+
:pattern,
|
31
|
+
keyword_init: true
|
32
|
+
) do
|
33
|
+
alias_method :active?, :active
|
34
|
+
|
35
|
+
# @return [Boolean] is this a matcher topic
|
36
|
+
def matcher?
|
37
|
+
type == :matcher
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Boolean] is this a discovered topic
|
41
|
+
def discovered?
|
42
|
+
type == :discovered
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [Boolean] is this a regular topic
|
46
|
+
def regular?
|
47
|
+
type == :regular
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module Routing
|
17
|
+
module Features
|
18
|
+
class Patterns < Base
|
19
|
+
# Expansion of the consumer groups routing component to work with patterns
|
20
|
+
module ConsumerGroup
|
21
|
+
# @param args [Object] whatever consumer group accepts
|
22
|
+
def initialize(*args)
|
23
|
+
super
|
24
|
+
@patterns = Patterns.new([])
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [::Karafka::Pro::Routing::Features::Patterns::Patterns] created patterns
|
28
|
+
def patterns
|
29
|
+
@patterns
|
30
|
+
end
|
31
|
+
|
32
|
+
# Creates the pattern for topic matching with appropriate virtual topic
|
33
|
+
# @param regexp_or_name [Symbol, String, Regexp] name of the pattern or regexp for
|
34
|
+
# automatic-based named patterns
|
35
|
+
# @param regexp [Regexp, nil] nil if we use auto-generated name based on the regexp or
|
36
|
+
# the regexp if we used named patterns
|
37
|
+
# @param block [Proc] appropriate underlying topic settings
|
38
|
+
def pattern=(regexp_or_name, regexp = nil, &block)
|
39
|
+
# This code allows us to have a nice nameless (automatic-named) patterns that do not
|
40
|
+
# have to be explicitly named. However if someone wants to use names for exclusions
|
41
|
+
# it can be done by providing both
|
42
|
+
if regexp_or_name.is_a?(Regexp)
|
43
|
+
name = nil
|
44
|
+
regexp = regexp_or_name
|
45
|
+
else
|
46
|
+
name = regexp_or_name
|
47
|
+
end
|
48
|
+
|
49
|
+
pattern = Pattern.new(name, regexp, block)
|
50
|
+
virtual_topic = public_send(:topic=, pattern.name, &block)
|
51
|
+
# Indicate the nature of this topic (matcher)
|
52
|
+
virtual_topic.patterns(active: true, type: :matcher, pattern: pattern)
|
53
|
+
pattern.topic = virtual_topic
|
54
|
+
@patterns << pattern
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [Hash] consumer group with patterns injected
|
58
|
+
def to_h
|
59
|
+
super.merge(
|
60
|
+
patterns: patterns.map(&:to_h)
|
61
|
+
).freeze
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module Routing
|
17
|
+
module Features
|
18
|
+
class Patterns < Base
|
19
|
+
module Contracts
|
20
|
+
# Contract to validate configuration of the filtering feature
|
21
|
+
class ConsumerGroup < Karafka::Contracts::Base
|
22
|
+
configure do |config|
|
23
|
+
config.error_messages = YAML.safe_load(
|
24
|
+
File.read(
|
25
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
26
|
+
)
|
27
|
+
).fetch('en').fetch('validations').fetch('consumer_group')
|
28
|
+
|
29
|
+
required(:patterns) { |val| val.is_a?(Array) && val.all? { |el| el.is_a?(Hash) } }
|
30
|
+
|
31
|
+
virtual do |data, errors|
|
32
|
+
next unless errors.empty?
|
33
|
+
|
34
|
+
validator = Pattern.new
|
35
|
+
|
36
|
+
data[:patterns].each do |pattern|
|
37
|
+
validator.validate!(pattern)
|
38
|
+
end
|
39
|
+
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
|
43
|
+
# Make sure, that there are no same regular expressions with different names
|
44
|
+
# in a single consumer group
|
45
|
+
virtual do |data, errors|
|
46
|
+
next unless errors.empty?
|
47
|
+
|
48
|
+
regexp_strings = data[:patterns].map { |pattern| pattern.fetch(:regexp_string) }
|
49
|
+
|
50
|
+
next if regexp_strings.empty?
|
51
|
+
next if regexp_strings.uniq.size == regexp_strings.size
|
52
|
+
|
53
|
+
[[%i[patterns], :regexps_not_unique]]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module Routing
|
17
|
+
module Features
|
18
|
+
class Patterns < Base
|
19
|
+
# Namespace for patterns related contracts
|
20
|
+
module Contracts
|
21
|
+
# Contract used to validate pattern data
|
22
|
+
class Pattern < Karafka::Contracts::Base
|
23
|
+
configure do |config|
|
24
|
+
config.error_messages = YAML.safe_load(
|
25
|
+
File.read(
|
26
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
27
|
+
)
|
28
|
+
).fetch('en').fetch('validations').fetch('pattern')
|
29
|
+
|
30
|
+
required(:regexp) { |val| val.is_a?(Regexp) }
|
31
|
+
|
32
|
+
required(:regexp_string) do |val|
|
33
|
+
val.is_a?(String) && val.start_with?('^') && val.size >= 2
|
34
|
+
end
|
35
|
+
|
36
|
+
required(:name) do |val|
|
37
|
+
val.is_a?(String) && Karafka::Contracts::TOPIC_REGEXP.match?(val)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module Routing
|
17
|
+
module Features
|
18
|
+
class Patterns < Base
|
19
|
+
# Namespace for patterns feature contracts
|
20
|
+
module Contracts
|
21
|
+
# Contract to validate configuration of the patterns feature
|
22
|
+
class Topic < Karafka::Contracts::Base
|
23
|
+
configure do |config|
|
24
|
+
config.error_messages = YAML.safe_load(
|
25
|
+
File.read(
|
26
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
27
|
+
)
|
28
|
+
).fetch('en').fetch('validations').fetch('topic')
|
29
|
+
end
|
30
|
+
|
31
|
+
nested(:patterns) do
|
32
|
+
required(:active) { |val| [true, false].include?(val) }
|
33
|
+
required(:type) { |val| %i[matcher discovered regular].include?(val) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module Routing
|
17
|
+
module Features
|
18
|
+
class Patterns < Base
|
19
|
+
# Detects if a given topic matches any of the patterns and if so, injects it into the
|
20
|
+
# given subscription group routing
|
21
|
+
#
|
22
|
+
# @note This is NOT thread-safe and should run in a thread-safe context that warranties
|
23
|
+
# that there won't be any race conditions
|
24
|
+
class Detector
|
25
|
+
# Checks if the provided topic matches any of the patterns and when detected, expands
|
26
|
+
# the routing with it.
|
27
|
+
#
|
28
|
+
# @param sg_topics [Array<Karafka::Routing::Topic>] given subscription group routing
|
29
|
+
# topics.
|
30
|
+
# @param new_topic [String] new topic that we have detected
|
31
|
+
def expand(sg_topics, new_topic)
|
32
|
+
sg_topics
|
33
|
+
.map(&:patterns)
|
34
|
+
.select(&:active?)
|
35
|
+
.select(&:matcher?)
|
36
|
+
.map(&:pattern)
|
37
|
+
.then { |pts| pts.empty? ? return : pts }
|
38
|
+
.then { |pts| Patterns.new(pts) }
|
39
|
+
.find(new_topic)
|
40
|
+
.then { |pattern| pattern || return }
|
41
|
+
.then { |pattern| install(pattern, new_topic, sg_topics) }
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Adds the discovered topic into the routing
|
47
|
+
#
|
48
|
+
# @param pattern [Karafka::Pro::Routing::Features::Patterns::Pattern] matched pattern
|
49
|
+
# @param discovered_topic [String] topic that we discovered that should be part of the
|
50
|
+
# routing from now on.
|
51
|
+
# @param sg_topics [Array<Karafka::Routing::Topic>]
|
52
|
+
def install(pattern, discovered_topic, sg_topics)
|
53
|
+
consumer_group = pattern.topic.consumer_group
|
54
|
+
|
55
|
+
# Build new topic and register within the consumer group
|
56
|
+
topic = consumer_group.public_send(:topic=, discovered_topic, &pattern.config)
|
57
|
+
topic.patterns(active: true, type: :discovered)
|
58
|
+
|
59
|
+
# Inject into subscription group topics array always, so everything is reflected
|
60
|
+
# there but since it is not active, will not be picked
|
61
|
+
sg_topics << topic
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module Routing
|
17
|
+
module Features
|
18
|
+
class Patterns < Base
|
19
|
+
# Karafka topic pattern object
|
20
|
+
# It represents a topic that is not yet materialized and that contains a name that is a
|
21
|
+
# regexp and not a "real" value. Underneath we define a dynamic topic, that is not
|
22
|
+
# active, that can be a subject to normal flow validations, etc.
|
23
|
+
class Pattern
|
24
|
+
# Pattern regexp
|
25
|
+
attr_accessor :regexp
|
26
|
+
|
27
|
+
# Each pattern has its own "topic" that we use as a routing reference that we define
|
28
|
+
# with non-existing topic for the routing to correctly pick it up for operations
|
29
|
+
# Virtual topic name for initial subscription
|
30
|
+
attr_reader :name
|
31
|
+
|
32
|
+
# Associated created virtual topic reference
|
33
|
+
attr_accessor :topic
|
34
|
+
|
35
|
+
# Config for real-topic configuration during injection
|
36
|
+
attr_reader :config
|
37
|
+
|
38
|
+
# @param name [String, Symbol, nil] name or the regexp for building the topic name or
|
39
|
+
# nil if we want to make it based on the regexp content
|
40
|
+
# @param regexp [Regexp] regular expression to match topics
|
41
|
+
# @param config [Proc] config for topic bootstrap
|
42
|
+
def initialize(name, regexp, config)
|
43
|
+
@regexp = regexp
|
44
|
+
# This name is also used as the underlying matcher topic name
|
45
|
+
#
|
46
|
+
# It can be used provided by the user in case user wants to use exclusions of topics
|
47
|
+
# or we can generate it if irrelevant.
|
48
|
+
#
|
49
|
+
# We generate it based on the regexp so within the same consumer group they are
|
50
|
+
# always unique (checked by topic validations)
|
51
|
+
#
|
52
|
+
# This will not prevent users from creating a different regexps matching the same
|
53
|
+
# topic but this minimizes simple mistakes
|
54
|
+
#
|
55
|
+
# This sub-part of sh1 should be unique enough and short-enough to use it here
|
56
|
+
digest = Digest::SHA1.hexdigest(safe_regexp.source)[8..16]
|
57
|
+
@name = name ? name.to_s : "karafka-pattern-#{digest}"
|
58
|
+
@config = config
|
59
|
+
end
|
60
|
+
|
61
|
+
# @return [String] defined regexp representation as a string that is compatible with
|
62
|
+
# librdkafka expectations. We use it as a subscription name for initial patterns
|
63
|
+
# subscription start.
|
64
|
+
def regexp_string
|
65
|
+
"^#{safe_regexp.source}"
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [Hash] hash representation of this routing pattern
|
69
|
+
def to_h
|
70
|
+
{
|
71
|
+
regexp: regexp,
|
72
|
+
name: name,
|
73
|
+
regexp_string: regexp_string
|
74
|
+
}.freeze
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
# Since pattern building happens before validations and we rely internally on the fact
|
80
|
+
# that regexp is provided and nothing else, we here "sanitize" the regexp for our
|
81
|
+
# internal usage. Karafka will not run anyhow because our post-routing contracts will
|
82
|
+
# prevent it from running but internally in this component we need to ensure, that
|
83
|
+
# prior to the validations we operate on a regexp
|
84
|
+
#
|
85
|
+
# @return [Regexp] returns a regexp always even if what we've received was not a regexp
|
86
|
+
def safe_regexp
|
87
|
+
# This regexp will never match anything
|
88
|
+
regexp.is_a?(Regexp) ? regexp : /$a/
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -15,20 +15,17 @@ module Karafka
|
|
15
15
|
module Pro
|
16
16
|
module Routing
|
17
17
|
module Features
|
18
|
-
class
|
19
|
-
#
|
20
|
-
class
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
nested(:delaying) do
|
30
|
-
required(:active) { |val| [true, false].include?(val) }
|
31
|
-
required(:delay) { |val| val.nil? || (val.is_a?(Integer) && val.positive?) }
|
18
|
+
class Patterns < Base
|
19
|
+
# Representation of groups of topics
|
20
|
+
class Patterns < ::Karafka::Routing::Topics
|
21
|
+
# Finds first pattern matching given topic name
|
22
|
+
#
|
23
|
+
# @param topic_name [String] topic name that may match a pattern
|
24
|
+
# @return [Karafka::Routing::Pattern, nil] pattern or nil if not found
|
25
|
+
# @note Please keep in mind, that there may be many patterns matching given topic name
|
26
|
+
# and we always pick the first one (defined first)
|
27
|
+
def find(topic_name)
|
28
|
+
@accumulator.find { |pattern| pattern.regexp =~ topic_name }
|
32
29
|
end
|
33
30
|
end
|
34
31
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module Routing
|
17
|
+
module Features
|
18
|
+
class Patterns < Base
|
19
|
+
# Patterns feature topic extensions
|
20
|
+
module Topic
|
21
|
+
# @return [String] subscription name or the regexp string representing matching of
|
22
|
+
# new topics that should be detected.
|
23
|
+
def subscription_name
|
24
|
+
patterns.active? && patterns.matcher? ? patterns.pattern.regexp_string : super
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param active [Boolean] is this topic active member of patterns
|
28
|
+
# @param type [Symbol] type of topic taking part in pattern matching
|
29
|
+
# @param pattern [Regexp] regular expression for matching
|
30
|
+
def patterns(active: false, type: :regular, pattern: nil)
|
31
|
+
@patterns ||= Config.new(active: active, type: type, pattern: pattern)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Boolean] is this topic a member of patterns
|
35
|
+
def patterns?
|
36
|
+
patterns.active?
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Hash] topic with all its native configuration options plus patterns
|
40
|
+
def to_h
|
41
|
+
super.merge(
|
42
|
+
patterns: patterns.to_h
|
43
|
+
).freeze
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|