karafka 2.1.12 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +41 -0
  4. data/Gemfile.lock +1 -1
  5. data/bin/record_rss +50 -0
  6. data/config/locales/errors.yml +4 -0
  7. data/config/locales/pro_errors.yml +17 -0
  8. data/lib/karafka/admin.rb +21 -33
  9. data/lib/karafka/connection/client.rb +1 -1
  10. data/lib/karafka/contracts/config.rb +24 -0
  11. data/lib/karafka/errors.rb +3 -0
  12. data/lib/karafka/instrumentation/vendors/datadog/logger_listener.rb +5 -2
  13. data/lib/karafka/messages/builders/message.rb +8 -4
  14. data/lib/karafka/pro/active_job/consumer.rb +1 -1
  15. data/lib/karafka/pro/cleaner/errors.rb +27 -0
  16. data/lib/karafka/pro/cleaner/messages/message.rb +46 -0
  17. data/lib/karafka/pro/cleaner/messages/messages.rb +42 -0
  18. data/lib/karafka/pro/cleaner.rb +41 -0
  19. data/lib/karafka/pro/contracts/base.rb +23 -0
  20. data/lib/karafka/pro/contracts/server_cli_options.rb +111 -0
  21. data/lib/karafka/pro/encryption/errors.rb +4 -1
  22. data/lib/karafka/pro/loader.rb +6 -2
  23. data/lib/karafka/pro/processing/strategies/dlq/default.rb +6 -0
  24. data/lib/karafka/pro/routing/features/active_job/builder.rb +45 -0
  25. data/lib/karafka/pro/routing/features/active_job.rb +26 -0
  26. data/lib/karafka/pro/routing/features/dead_letter_queue/contracts/topic.rb +53 -0
  27. data/lib/karafka/pro/routing/features/delaying/contracts/topic.rb +41 -0
  28. data/lib/karafka/pro/routing/features/expiring/contracts/topic.rb +41 -0
  29. data/lib/karafka/pro/routing/features/filtering/contracts/topic.rb +44 -0
  30. data/lib/karafka/pro/routing/features/long_running_job/{contract.rb → contracts/topic.rb} +14 -11
  31. data/lib/karafka/pro/routing/features/{filtering/contract.rb → patterns/builder.rb} +13 -16
  32. data/lib/karafka/pro/routing/features/patterns/config.rb +54 -0
  33. data/lib/karafka/pro/routing/features/patterns/consumer_group.rb +68 -0
  34. data/lib/karafka/pro/routing/features/patterns/contracts/consumer_group.rb +62 -0
  35. data/lib/karafka/pro/routing/features/patterns/contracts/pattern.rb +46 -0
  36. data/lib/karafka/pro/routing/features/patterns/contracts/topic.rb +41 -0
  37. data/lib/karafka/pro/routing/features/patterns/detector.rb +68 -0
  38. data/lib/karafka/pro/routing/features/patterns/pattern.rb +81 -0
  39. data/lib/karafka/pro/routing/features/{delaying/contract.rb → patterns/patterns.rb} +11 -14
  40. data/lib/karafka/pro/routing/features/patterns/topic.rb +50 -0
  41. data/lib/karafka/pro/routing/features/patterns/topics.rb +53 -0
  42. data/lib/karafka/pro/routing/features/patterns.rb +33 -0
  43. data/lib/karafka/pro/routing/features/pausing/contracts/topic.rb +51 -0
  44. data/lib/karafka/pro/routing/features/throttling/contracts/topic.rb +44 -0
  45. data/lib/karafka/pro/routing/features/virtual_partitions/contracts/topic.rb +55 -0
  46. data/lib/karafka/routing/consumer_group.rb +1 -1
  47. data/lib/karafka/routing/features/active_job/contracts/topic.rb +44 -0
  48. data/lib/karafka/routing/features/active_job/proxy.rb +14 -0
  49. data/lib/karafka/routing/features/base/expander.rb +8 -2
  50. data/lib/karafka/routing/features/base.rb +4 -2
  51. data/lib/karafka/routing/features/dead_letter_queue/contracts/topic.rb +46 -0
  52. data/lib/karafka/routing/features/declaratives/contracts/topic.rb +33 -0
  53. data/lib/karafka/routing/features/manual_offset_management/contracts/topic.rb +27 -0
  54. data/lib/karafka/routing/router.rb +0 -11
  55. data/lib/karafka/routing/subscription_group.rb +9 -0
  56. data/lib/karafka/routing/topic.rb +5 -0
  57. data/lib/karafka/server.rb +9 -4
  58. data/lib/karafka/setup/config.rb +45 -0
  59. data/lib/karafka/version.rb +1 -1
  60. data.tar.gz.sig +0 -0
  61. metadata +37 -15
  62. metadata.gz.sig +0 -0
  63. data/lib/karafka/pro/routing/features/dead_letter_queue/contract.rb +0 -50
  64. data/lib/karafka/pro/routing/features/expiring/contract.rb +0 -38
  65. data/lib/karafka/pro/routing/features/pausing/contract.rb +0 -48
  66. data/lib/karafka/pro/routing/features/throttling/contract.rb +0 -41
  67. data/lib/karafka/pro/routing/features/virtual_partitions/contract.rb +0 -52
  68. data/lib/karafka/routing/features/active_job/contract.rb +0 -41
  69. data/lib/karafka/routing/features/dead_letter_queue/contract.rb +0 -42
  70. data/lib/karafka/routing/features/declaratives/contract.rb +0 -30
  71. data/lib/karafka/routing/features/manual_offset_management/contract.rb +0 -24
@@ -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,81 @@
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(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
+ "^#{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
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -15,20 +15,17 @@ module Karafka
15
15
  module Pro
16
16
  module Routing
17
17
  module Features
18
- class Delaying < Base
19
- # Contract to validate configuration of the expiring feature
20
- class Contract < Contracts::Base
21
- configure do |config|
22
- config.error_messages = YAML.safe_load(
23
- File.read(
24
- File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
25
- )
26
- ).fetch('en').fetch('validations').fetch('topic')
27
- end
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
@@ -0,0 +1,53 @@
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 Topics
21
+ # Finds topic by its name in a more extensive way than the regular. Regular uses the
22
+ # pre-existing topics definitions. This extension also runs the expansion based on
23
+ # defined routing patterns (if any)
24
+ #
25
+ # If topic does not exist, it will try to run discovery in case there are patterns
26
+ # defined that would match it.
27
+ # This allows us to support lookups for newly appearing topics based on their regexp
28
+ # patterns.
29
+ #
30
+ # @param topic_name [String] topic name
31
+ # @return [Karafka::Routing::Topic]
32
+ # @raise [Karafka::Errors::TopicNotFoundError] this should never happen. If you see it,
33
+ # please create an issue.
34
+ #
35
+ # @note This method should not be used in context of finding multiple missing topics in
36
+ # loops because it catches exceptions and attempts to expand routes. If this is used
37
+ # in a loop for lookups on thousands of topics with detector expansion, this may
38
+ # be slow. It should be used in the context where newly discovered topics are found
39
+ # and should by design match a pattern. For quick lookups on batches of topics, it
40
+ # is recommended to use a custom built lookup with conditional expander.
41
+ def find(topic_name)
42
+ super
43
+ rescue Karafka::Errors::TopicNotFoundError
44
+ Detector.new.expand(self, topic_name)
45
+
46
+ super
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,33 @@
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
+ # Dynamic topics builder feature.
19
+ #
20
+ # Allows you to define patterns in routes that would then automatically subscribe and
21
+ # start consuming new topics.
22
+ #
23
+ # This feature works by injecting a topic that represents a regexp subscription (matcher)
24
+ # that at the same time holds the builder block for full config of a newly detected topic.
25
+ #
26
+ # We inject a virtual topic to hold settings but also to be able to run validations
27
+ # during boot to ensure consistency of the pattern base setup.
28
+ class Patterns < Base
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,51 @@
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 Pausing < Base
19
+ # Namespace for pausing feature
20
+ module Contracts
21
+ # Contract to make sure, that the pause settings on a per topic basis are as expected
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
+
30
+ required(:pause_timeout) { |val| val.is_a?(Integer) && val.positive? }
31
+ required(:pause_max_timeout) { |val| val.is_a?(Integer) && val.positive? }
32
+ required(:pause_with_exponential_backoff) { |val| [true, false].include?(val) }
33
+
34
+ virtual do |data, errors|
35
+ next unless errors.empty?
36
+
37
+ pause_timeout = data.fetch(:pause_timeout)
38
+ pause_max_timeout = data.fetch(:pause_max_timeout)
39
+
40
+ next if pause_timeout <= pause_max_timeout
41
+
42
+ [[%i[pause_timeout], :max_timeout_vs_pause_max_timeout]]
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -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 Throttling < Base
19
+ # Namespace for throttling contracts
20
+ module Contracts
21
+ # Rules around throttling 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
30
+
31
+ nested(:throttling) do
32
+ required(:active) { |val| [true, false].include?(val) }
33
+ required(:interval) { |val| val.is_a?(Integer) && val.positive? }
34
+ required(:limit) do |val|
35
+ (val.is_a?(Integer) || val == Float::INFINITY) && val.positive?
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,55 @@
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 VirtualPartitions < Base
19
+ # Namespace for VP contracts
20
+ module Contracts
21
+ # Rules around virtual partitions
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(:virtual_partitions) do
32
+ required(:active) { |val| [true, false].include?(val) }
33
+ required(:partitioner) { |val| val.nil? || val.respond_to?(:call) }
34
+ required(:max_partitions) { |val| val.is_a?(Integer) && val >= 1 }
35
+ end
36
+
37
+ # When virtual partitions are defined, partitioner needs to respond to `#call` and it
38
+ # cannot be nil
39
+ virtual do |data, errors|
40
+ next unless errors.empty?
41
+
42
+ virtual_partitions = data[:virtual_partitions]
43
+
44
+ next unless virtual_partitions[:active]
45
+ next if virtual_partitions[:partitioner].respond_to?(:call)
46
+
47
+ [[%i[virtual_partitions partitioner], :respond_to_call]]
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -55,7 +55,7 @@ module Karafka
55
55
  def subscription_group=(name = SubscriptionGroup.id, &block)
56
56
  # We cast it here, so the routing supports symbol based but that's anyhow later on
57
57
  # validated as a string
58
- @current_subscription_group_id = name
58
+ @current_subscription_group_id = name.to_s
59
59
 
60
60
  Proxy.new(self, &block)
61
61
 
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Routing
5
+ module Features
6
+ class ActiveJob < Base
7
+ # This feature validation contracts
8
+ module Contracts
9
+ # Rules around using ActiveJob routing - basically you need to have ActiveJob available
10
+ # in order to be able to use active job routing
11
+ class Topic < Karafka::Contracts::Base
12
+ configure do |config|
13
+ config.error_messages = YAML.safe_load(
14
+ File.read(
15
+ File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
16
+ )
17
+ ).fetch('en').fetch('validations').fetch('topic')
18
+ end
19
+
20
+ virtual do |data, errors|
21
+ next unless errors.empty?
22
+ next unless data[:active_job][:active]
23
+ # One should not define active job jobs without ActiveJob being available for usage
24
+ next if Object.const_defined?('ActiveJob::Base')
25
+
26
+ [[%i[consumer], :active_job_missing]]
27
+ end
28
+
29
+ # ActiveJob needs to always run with manual offset management
30
+ # Automatic offset management cannot work with ActiveJob. Otherwise we could mark as
31
+ # consumed jobs that did not run because of shutdown.
32
+ virtual do |data, errors|
33
+ next unless errors.empty?
34
+ next unless data[:active_job][:active]
35
+ next if data[:manual_offset_management][:active]
36
+
37
+ [[%i[manual_offset_management], :must_be_enabled]]
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Routing
5
+ module Features
6
+ class ActiveJob < Base
7
+ # Routing proxy extensions for ActiveJob
8
+ module Proxy
9
+ include Builder
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -30,15 +30,21 @@ module Karafka
30
30
  scope = @scope
31
31
 
32
32
  Module.new do
33
- # Runs validations related to this feature on a topic
33
+ # Runs validations related to this feature on a routing resources
34
34
  #
35
35
  # @param block [Proc] routing defining block
36
36
  define_method :draw do |&block|
37
37
  result = super(&block)
38
38
 
39
39
  each do |consumer_group|
40
+ if scope::Contracts.const_defined?('ConsumerGroup', false)
41
+ scope::Contracts::ConsumerGroup.new.validate!(consumer_group.to_h)
42
+ end
43
+
44
+ next unless scope::Contracts.const_defined?('Topic', false)
45
+
40
46
  consumer_group.topics.each do |topic|
41
- scope::Contract.new.validate!(topic.to_h)
47
+ scope::Contracts::Topic.new.validate!(topic.to_h)
42
48
  end
43
49
  end
44
50
 
@@ -14,9 +14,11 @@ module Karafka
14
14
  # Extends topic and builder with given feature API
15
15
  def activate
16
16
  Topic.prepend(self::Topic) if const_defined?('Topic', false)
17
- Proxy.prepend(self::Builder) if const_defined?('Builder', false)
17
+ Topics.prepend(self::Topics) if const_defined?('Topics', false)
18
+ ConsumerGroup.prepend(self::ConsumerGroup) if const_defined?('ConsumerGroup', false)
19
+ Proxy.prepend(self::Proxy) if const_defined?('Proxy', false)
18
20
  Builder.prepend(self::Builder) if const_defined?('Builder', false)
19
- Builder.prepend(Base::Expander.new(self)) if const_defined?('Contract', false)
21
+ Builder.prepend(Base::Expander.new(self)) if const_defined?('Contracts', false)
20
22
  end
21
23
 
22
24
  # Loads all the features and activates them