karafka 2.1.12 → 2.2.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.
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