karafka 2.2.13 → 2.3.0.alpha1

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 (125) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/ci.yml +38 -12
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +161 -125
  6. data/Gemfile.lock +12 -12
  7. data/README.md +0 -2
  8. data/SECURITY.md +23 -0
  9. data/config/locales/errors.yml +7 -1
  10. data/config/locales/pro_errors.yml +22 -0
  11. data/docker-compose.yml +3 -1
  12. data/karafka.gemspec +2 -2
  13. data/lib/karafka/admin/acl.rb +287 -0
  14. data/lib/karafka/admin.rb +118 -16
  15. data/lib/karafka/app.rb +12 -3
  16. data/lib/karafka/base_consumer.rb +32 -31
  17. data/lib/karafka/cli/base.rb +1 -1
  18. data/lib/karafka/connection/client.rb +94 -84
  19. data/lib/karafka/connection/conductor.rb +28 -0
  20. data/lib/karafka/connection/listener.rb +165 -46
  21. data/lib/karafka/connection/listeners_batch.rb +5 -11
  22. data/lib/karafka/connection/manager.rb +72 -0
  23. data/lib/karafka/connection/messages_buffer.rb +12 -0
  24. data/lib/karafka/connection/proxy.rb +17 -0
  25. data/lib/karafka/connection/status.rb +75 -0
  26. data/lib/karafka/contracts/config.rb +14 -10
  27. data/lib/karafka/contracts/consumer_group.rb +9 -1
  28. data/lib/karafka/contracts/topic.rb +3 -1
  29. data/lib/karafka/errors.rb +13 -0
  30. data/lib/karafka/instrumentation/assignments_tracker.rb +96 -0
  31. data/lib/karafka/instrumentation/callbacks/rebalance.rb +10 -7
  32. data/lib/karafka/instrumentation/logger_listener.rb +3 -9
  33. data/lib/karafka/instrumentation/notifications.rb +19 -9
  34. data/lib/karafka/instrumentation/vendors/appsignal/metrics_listener.rb +31 -28
  35. data/lib/karafka/instrumentation/vendors/datadog/logger_listener.rb +22 -3
  36. data/lib/karafka/instrumentation/vendors/datadog/metrics_listener.rb +15 -12
  37. data/lib/karafka/instrumentation/vendors/kubernetes/liveness_listener.rb +39 -36
  38. data/lib/karafka/pro/base_consumer.rb +47 -0
  39. data/lib/karafka/pro/connection/manager.rb +300 -0
  40. data/lib/karafka/pro/connection/multiplexing/listener.rb +40 -0
  41. data/lib/karafka/pro/instrumentation/performance_tracker.rb +85 -0
  42. data/lib/karafka/pro/iterator/tpl_builder.rb +1 -1
  43. data/lib/karafka/pro/iterator.rb +1 -6
  44. data/lib/karafka/pro/loader.rb +16 -2
  45. data/lib/karafka/pro/processing/coordinator.rb +2 -1
  46. data/lib/karafka/pro/processing/executor.rb +37 -0
  47. data/lib/karafka/pro/processing/expansions_selector.rb +32 -0
  48. data/lib/karafka/pro/processing/jobs/periodic.rb +41 -0
  49. data/lib/karafka/pro/processing/jobs/periodic_non_blocking.rb +32 -0
  50. data/lib/karafka/pro/processing/jobs_builder.rb +14 -3
  51. data/lib/karafka/pro/processing/offset_metadata/consumer.rb +44 -0
  52. data/lib/karafka/pro/processing/offset_metadata/fetcher.rb +131 -0
  53. data/lib/karafka/pro/processing/offset_metadata/listener.rb +46 -0
  54. data/lib/karafka/pro/processing/schedulers/base.rb +143 -0
  55. data/lib/karafka/pro/processing/schedulers/default.rb +107 -0
  56. data/lib/karafka/pro/processing/strategies/aj/lrj_mom_vp.rb +1 -1
  57. data/lib/karafka/pro/processing/strategies/default.rb +136 -3
  58. data/lib/karafka/pro/processing/strategies/dlq/default.rb +35 -0
  59. data/lib/karafka/pro/processing/strategies/lrj/default.rb +1 -1
  60. data/lib/karafka/pro/processing/strategies/lrj/mom.rb +1 -1
  61. data/lib/karafka/pro/processing/strategies/vp/default.rb +60 -26
  62. data/lib/karafka/pro/processing/virtual_offset_manager.rb +41 -11
  63. data/lib/karafka/pro/routing/features/long_running_job/topic.rb +2 -0
  64. data/lib/karafka/pro/routing/features/multiplexing/config.rb +38 -0
  65. data/lib/karafka/pro/routing/features/multiplexing/contracts/topic.rb +114 -0
  66. data/lib/karafka/pro/routing/features/multiplexing/patches/contracts/consumer_group.rb +42 -0
  67. data/lib/karafka/pro/routing/features/multiplexing/proxy.rb +38 -0
  68. data/lib/karafka/pro/routing/features/multiplexing/subscription_group.rb +42 -0
  69. data/lib/karafka/pro/routing/features/multiplexing/subscription_groups_builder.rb +40 -0
  70. data/lib/karafka/pro/routing/features/multiplexing.rb +59 -0
  71. data/lib/karafka/pro/routing/features/non_blocking_job/topic.rb +32 -0
  72. data/lib/karafka/pro/routing/features/non_blocking_job.rb +37 -0
  73. data/lib/karafka/pro/routing/features/offset_metadata/config.rb +33 -0
  74. data/lib/karafka/pro/routing/features/offset_metadata/contracts/topic.rb +42 -0
  75. data/lib/karafka/pro/routing/features/offset_metadata/topic.rb +65 -0
  76. data/lib/karafka/pro/routing/features/offset_metadata.rb +40 -0
  77. data/lib/karafka/pro/routing/features/patterns/contracts/consumer_group.rb +4 -0
  78. data/lib/karafka/pro/routing/features/patterns/detector.rb +18 -10
  79. data/lib/karafka/pro/routing/features/periodic_job/config.rb +37 -0
  80. data/lib/karafka/pro/routing/features/periodic_job/contracts/topic.rb +44 -0
  81. data/lib/karafka/pro/routing/features/periodic_job/topic.rb +94 -0
  82. data/lib/karafka/pro/routing/features/periodic_job.rb +27 -0
  83. data/lib/karafka/pro/routing/features/virtual_partitions/config.rb +1 -0
  84. data/lib/karafka/pro/routing/features/virtual_partitions/contracts/topic.rb +1 -0
  85. data/lib/karafka/pro/routing/features/virtual_partitions/topic.rb +7 -2
  86. data/lib/karafka/process.rb +5 -3
  87. data/lib/karafka/processing/coordinator.rb +5 -1
  88. data/lib/karafka/processing/executor.rb +43 -13
  89. data/lib/karafka/processing/executors_buffer.rb +22 -7
  90. data/lib/karafka/processing/jobs/base.rb +19 -2
  91. data/lib/karafka/processing/jobs/consume.rb +3 -3
  92. data/lib/karafka/processing/jobs/idle.rb +5 -0
  93. data/lib/karafka/processing/jobs/revoked.rb +5 -0
  94. data/lib/karafka/processing/jobs/shutdown.rb +5 -0
  95. data/lib/karafka/processing/jobs_queue.rb +19 -8
  96. data/lib/karafka/processing/schedulers/default.rb +42 -0
  97. data/lib/karafka/processing/strategies/base.rb +13 -4
  98. data/lib/karafka/processing/strategies/default.rb +23 -7
  99. data/lib/karafka/processing/strategies/dlq.rb +36 -0
  100. data/lib/karafka/processing/worker.rb +4 -1
  101. data/lib/karafka/routing/builder.rb +12 -2
  102. data/lib/karafka/routing/consumer_group.rb +5 -5
  103. data/lib/karafka/routing/features/base.rb +44 -8
  104. data/lib/karafka/routing/features/dead_letter_queue/config.rb +6 -1
  105. data/lib/karafka/routing/features/dead_letter_queue/contracts/topic.rb +1 -0
  106. data/lib/karafka/routing/features/dead_letter_queue/topic.rb +9 -2
  107. data/lib/karafka/routing/proxy.rb +4 -3
  108. data/lib/karafka/routing/subscription_group.rb +2 -2
  109. data/lib/karafka/routing/subscription_groups_builder.rb +11 -2
  110. data/lib/karafka/routing/topic.rb +8 -10
  111. data/lib/karafka/routing/topics.rb +1 -1
  112. data/lib/karafka/runner.rb +13 -3
  113. data/lib/karafka/server.rb +5 -9
  114. data/lib/karafka/setup/config.rb +21 -1
  115. data/lib/karafka/status.rb +23 -14
  116. data/lib/karafka/templates/karafka.rb.erb +7 -0
  117. data/lib/karafka/time_trackers/partition_usage.rb +56 -0
  118. data/lib/karafka/version.rb +1 -1
  119. data.tar.gz.sig +0 -0
  120. metadata +47 -13
  121. metadata.gz.sig +0 -0
  122. data/lib/karafka/connection/consumer_group_coordinator.rb +0 -48
  123. data/lib/karafka/pro/performance_tracker.rb +0 -84
  124. data/lib/karafka/pro/processing/scheduler.rb +0 -74
  125. data/lib/karafka/processing/scheduler.rb +0 -38
@@ -0,0 +1,32 @@
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 NonBlockingJob < Base
19
+ # Non-Blocking Jobs topic API extensions
20
+ module Topic
21
+ # @param args [Array] anything accepted by the `#long_running_job` API
22
+ def non_blocking_job(*args)
23
+ long_running_job(*args)
24
+ end
25
+
26
+ alias non_blocking non_blocking_job
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,37 @@
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
+ # Namespace for Pro routing enhancements
17
+ module Routing
18
+ # Namespace for additional Pro features
19
+ module Features
20
+ # Non Blocking Job is just an alias for LRJ.
21
+ #
22
+ # We however have it as a separate feature because its use-case may vary from LRJ.
23
+ #
24
+ # While LRJ is used mainly for long-running jobs that would take more than max poll
25
+ # interval time, non-blocking can be applied to make sure that we do not wait with polling
26
+ # of different partitions and topics that are subscribed together.
27
+ #
28
+ # This effectively allows for better resources utilization
29
+ #
30
+ # All the underlying code is the same but use-case is different and this should be
31
+ # reflected in the routing, hence this "virtual" feature.
32
+ class NonBlockingJob < Base
33
+ end
34
+ end
35
+ end
36
+ end
37
+ 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
+ class OffsetMetadata < Base
19
+ # Config for commit metadata feature
20
+ Config = Struct.new(
21
+ :active,
22
+ :deserializer,
23
+ :cache,
24
+ keyword_init: true
25
+ ) do
26
+ alias_method :active?, :active
27
+ alias_method :cache?, :cache
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,42 @@
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 OffsetMetadata < Base
19
+ # Namespace for offset metadata feature contracts
20
+ module Contracts
21
+ # Contract to validate configuration of the expiring 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(:offset_metadata) do
32
+ required(:active) { |val| val == true }
33
+ required(:cache) { |val| [true, false].include?(val) }
34
+ required(:deserializer) { |val| val.respond_to?(:call) }
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,65 @@
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
+ # This feature allows for saving and retrieving offset metadata with custom deserialization
19
+ # support. It allows for storing extra data during commits that can be then used to alter
20
+ # the processing flow after a rebalance.
21
+ #
22
+ # @note Because this feature has zero performance impact and makes no queries to Kafka
23
+ # unless requested, it is always enabled.
24
+ class OffsetMetadata < Base
25
+ # Empty string not to create it on each deserialization
26
+ EMPTY_STRING = ''
27
+
28
+ # Default deserializer just ensures we always get a string as without metadata by
29
+ # default it would be nil
30
+ STRING_DESERIALIZER = ->(raw_metadata) { raw_metadata || EMPTY_STRING }.freeze
31
+
32
+ private_constant :STRING_DESERIALIZER, :EMPTY_STRING
33
+
34
+ # Commit Metadata API extensions
35
+ module Topic
36
+ # @param cache [Boolean] should we cache the response until rebalance
37
+ # @param deserializer [#call] deserializer that will get raw data and should return
38
+ # deserialized metadata
39
+ # @return [Config] this feature config
40
+ def offset_metadata(cache: true, deserializer: STRING_DESERIALIZER)
41
+ @offset_metadata ||= Config.new(
42
+ active: true,
43
+ cache: cache,
44
+ deserializer: deserializer
45
+ )
46
+ end
47
+
48
+ # @return [true] is offset metadata active (it always is)
49
+ def offset_metadata?
50
+ offset_metadata.active?
51
+ end
52
+
53
+ # @return [Hash] topic with all its native configuration options plus offset metadata
54
+ # settings
55
+ def to_h
56
+ super.merge(
57
+ offset_metadata: offset_metadata.to_h
58
+ ).freeze
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,40 @@
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
+ # Offset Metadata Support with a custom deserializer
19
+ class OffsetMetadata < Base
20
+ class << self
21
+ # If needed installs the needed listener and initializes tracker
22
+ #
23
+ # @param _config [Karafka::Core::Configurable::Node] app config
24
+ def post_setup(_config)
25
+ ::Karafka::App.monitor.subscribe('app.running') do
26
+ # Initialize the tracker prior to becoming multi-threaded
27
+ ::Karafka::Processing::InlineInsights::Tracker.instance
28
+
29
+ # Subscribe to the statistics reports and collect them
30
+ ::Karafka.monitor.subscribe(
31
+ ::Karafka::Pro::Processing::OffsetMetadata::Listener.new
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -46,7 +46,11 @@ module Karafka
46
46
  next unless errors.empty?
47
47
 
48
48
  regexp_strings = data[:patterns].map { |pattern| pattern.fetch(:regexp_string) }
49
+ regexp_names = data[:patterns].map { |pattern| pattern.fetch(:name) }
49
50
 
51
+ # If all names are the same for the same regexp, it means its a multiplex and
52
+ # we can allow it
53
+ next if regexp_names.uniq.size == 1 && regexp_strings.uniq.size == 1
50
54
  next if regexp_strings.empty?
51
55
  next if regexp_strings.uniq.size == regexp_strings.size
52
56
 
@@ -22,6 +22,12 @@ module Karafka
22
22
  # @note This is NOT thread-safe and should run in a thread-safe context that warranties
23
23
  # that there won't be any race conditions
24
24
  class Detector
25
+ # Mutex for making sure that we do not modify same consumer group in runtime at the
26
+ # same time from multiple subscription groups if they operate in a multiplexed mode
27
+ MUTEX = Mutex.new
28
+
29
+ private_constant :MUTEX
30
+
25
31
  # Checks if the provided topic matches any of the patterns and when detected, expands
26
32
  # the routing with it.
27
33
  #
@@ -29,16 +35,18 @@ module Karafka
29
35
  # topics.
30
36
  # @param new_topic [String] new topic that we have detected
31
37
  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) }
38
+ MUTEX.synchronize do
39
+ sg_topics
40
+ .map(&:patterns)
41
+ .select(&:active?)
42
+ .select(&:matcher?)
43
+ .map(&:pattern)
44
+ .then { |pts| pts.empty? ? return : pts }
45
+ .then { |pts| Patterns.new(pts) }
46
+ .find(new_topic)
47
+ .then { |pattern| pattern || return }
48
+ .then { |pattern| install(pattern, new_topic, sg_topics) }
49
+ end
42
50
  end
43
51
 
44
52
  private
@@ -0,0 +1,37 @@
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 PeriodicJob < Base
19
+ # Config for periodics topics feature
20
+ Config = Struct.new(
21
+ :active,
22
+ :during_pause,
23
+ :during_retry,
24
+ :interval,
25
+ :materialized,
26
+ keyword_init: true
27
+ ) do
28
+ alias_method :active?, :active
29
+ alias_method :during_pause?, :during_pause
30
+ alias_method :during_retry?, :during_retry
31
+ alias_method :materialized?, :materialized
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ 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 PeriodicJob < Base
19
+ # Namespace for periodics messages contracts
20
+ module Contracts
21
+ # Contract to validate configuration of the periodics 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(:periodic_job) do
32
+ required(:active) { |val| [true, false].include?(val) }
33
+ required(:interval) { |val| val.is_a?(Integer) && val >= 100 }
34
+ required(:during_pause) { |val| [true, false].include?(val) }
35
+ required(:during_retry) { |val| [true, false].include?(val) }
36
+ required(:materialized) { |val| [true, false].include?(val) }
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,94 @@
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 PeriodicJob < Base
19
+ # Periodic topic action flows extensions
20
+ module Topic
21
+ # Defines topic as periodic. Periodic topics consumers will invoke `#tick` with each
22
+ # poll where messages were not received.
23
+ # @param active [Boolean] should ticking happen for this topic assignments.
24
+ # @param interval [Integer] minimum interval to run periodic jobs on given topic.
25
+ # @param during_pause [Boolean, nil] Should periodic jobs run when partition is paused.
26
+ # It is set to `nil` by default allowing for detection when this value is not
27
+ # configured but should be built dynamically based on LRJ status.
28
+ # @param during_retry [Boolean, nil] Should we run when there was an error and we are
29
+ # in a retry flow. Please note that for this to work, `during_pause` also needs to be
30
+ # set to true as errors retry happens after pause.
31
+ def periodic_job(
32
+ active = false,
33
+ interval: nil,
34
+ during_pause: nil,
35
+ during_retry: nil
36
+ )
37
+ @periodic_job ||= begin
38
+ # Set to active if any of the values was configured
39
+ active = true unless interval.nil?
40
+ active = true unless during_pause.nil?
41
+ active = true unless during_retry.nil?
42
+ # Default is not to retry during retry flow
43
+ during_retry = false if during_retry.nil?
44
+
45
+ # If no interval, use default
46
+ interval ||= ::Karafka::App.config.internal.tick_interval
47
+
48
+ Config.new(
49
+ active: active,
50
+ interval: interval,
51
+ during_pause: during_pause,
52
+ during_retry: during_retry,
53
+ # This is internal setting for state management, not part of the configuration
54
+ # Do not overwrite.
55
+ # If `during_pause` is explicit, we do not select it based on LRJ setup and we
56
+ # consider if fully ready out of the box
57
+ materialized: !during_pause.nil?
58
+ )
59
+ end
60
+
61
+ return @periodic_job if @periodic_job.materialized?
62
+ return @periodic_job unless @long_running_job
63
+
64
+ # If not configured in any way, we want not to process during pause for LRJ.
65
+ # LRJ pauses by default when processing and during this time we do not want to
66
+ # tick at all. This prevents us from running periodic jobs while LRJ jobs are
67
+ # running. This of course has a side effect of not running when paused for any
68
+ # other reason but it is a compromise in the default settings
69
+ @periodic_job.during_pause = !long_running_job?
70
+ @periodic_job.materialized = true
71
+
72
+ @periodic_job
73
+ end
74
+
75
+ alias periodic periodic_job
76
+
77
+ # @return [Boolean] is periodics active
78
+ def periodic_job?
79
+ periodic_job.active?
80
+ end
81
+
82
+ # @return [Hash] topic with all its native configuration options plus periodics flows
83
+ # settings
84
+ def to_h
85
+ super.merge(
86
+ periodic_job: periodic_job.to_h
87
+ ).freeze
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,27 @@
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
+ # Feature allowing to run consumer operations even when no data is present on periodic
19
+ # interval.
20
+ # This allows for advanced window-based operations regardless of income of new data and
21
+ # other advanced cases where the consumer is needed even when no data is coming
22
+ class PeriodicJob < Base
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -21,6 +21,7 @@ module Karafka
21
21
  :active,
22
22
  :partitioner,
23
23
  :max_partitions,
24
+ :offset_metadata_strategy,
24
25
  keyword_init: true
25
26
  ) { alias_method :active?, :active }
26
27
  end
@@ -32,6 +32,7 @@ module Karafka
32
32
  required(:active) { |val| [true, false].include?(val) }
33
33
  required(:partitioner) { |val| val.nil? || val.respond_to?(:call) }
34
34
  required(:max_partitions) { |val| val.is_a?(Integer) && val >= 1 }
35
+ required(:offset_metadata_strategy) { |val| %i[exact current].include?(val) }
35
36
  end
36
37
 
37
38
  # When virtual partitions are defined, partitioner needs to respond to `#call` and it
@@ -23,16 +23,21 @@ module Karafka
23
23
  # create more work than workers. When less, can ensure we have spare resources to
24
24
  # process other things in parallel.
25
25
  # @param partitioner [nil, #call] nil or callable partitioner
26
+ # @param offset_metadata_strategy [Symbol] how we should match the metadata for the
27
+ # offset. `:exact` will match the offset matching metadata and `:current` will select
28
+ # the most recently reported metadata
26
29
  # @return [VirtualPartitions] method that allows to set the virtual partitions details
27
30
  # during the routing configuration and then allows to retrieve it
28
31
  def virtual_partitions(
29
32
  max_partitions: Karafka::App.config.concurrency,
30
- partitioner: nil
33
+ partitioner: nil,
34
+ offset_metadata_strategy: :current
31
35
  )
32
36
  @virtual_partitions ||= Config.new(
33
37
  active: !partitioner.nil?,
34
38
  max_partitions: max_partitions,
35
- partitioner: partitioner
39
+ partitioner: partitioner,
40
+ offset_metadata_strategy: offset_metadata_strategy
36
41
  )
37
42
  end
38
43
 
@@ -25,9 +25,11 @@ module Karafka
25
25
  # Karafka.logger.info('Log something here')
26
26
  # exit
27
27
  # end
28
- define_method :"on_#{signal.to_s.downcase}" do |&block|
29
- @callbacks[signal] << block
30
- end
28
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
29
+ def on_#{signal.to_s.downcase}(&block)
30
+ @callbacks[:#{signal}] << block
31
+ end
32
+ RUBY
31
33
  end
32
34
 
33
35
  # Creates an instance of process and creates empty hash for callbacks
@@ -10,8 +10,12 @@ module Karafka
10
10
  # listener thread, but we go with thread-safe by default for all not to worry about potential
11
11
  # future mistakes.
12
12
  class Coordinator
13
+ extend Forwardable
14
+
13
15
  attr_reader :pause_tracker, :seek_offset, :topic, :partition
14
16
 
17
+ def_delegators :@pause_tracker, :attempt, :paused?
18
+
15
19
  # @param topic [Karafka::Routing::Topic]
16
20
  # @param partition [Integer]
17
21
  # @param pause_tracker [Karafka::TimeTrackers::Pause] pause tracker for given topic partition
@@ -149,7 +153,7 @@ module Karafka
149
153
 
150
154
  # @return [Boolean] are we in a pause that was initiated by the user
151
155
  def manual_pause?
152
- @pause_tracker.paused? && @manual_pause
156
+ paused? && @manual_pause
153
157
  end
154
158
 
155
159
  # Marks seek as manual for coordination purposes