karafka 2.6.0.beta1 → 2.6.0.rc2

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 (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -27
  3. data/certs/expired.txt +2 -0
  4. data/config/locales/errors.yml +5 -0
  5. data/config/locales/pro_errors.yml +5 -5
  6. data/karafka.gemspec +1 -1
  7. data/lib/active_job/queue_adapters/karafka_adapter.rb +1 -1
  8. data/lib/karafka/active_job/current_attributes/loading.rb +1 -1
  9. data/lib/karafka/active_job/current_attributes.rb +4 -2
  10. data/lib/karafka/active_job/dispatcher.rb +1 -2
  11. data/lib/karafka/admin/consumer_groups.rb +8 -2
  12. data/lib/karafka/admin/replication.rb +6 -3
  13. data/lib/karafka/admin/topics.rb +32 -12
  14. data/lib/karafka/admin.rb +155 -59
  15. data/lib/karafka/app.rb +1 -1
  16. data/lib/karafka/base_consumer.rb +33 -5
  17. data/lib/karafka/cli/console.rb +0 -1
  18. data/lib/karafka/cli/help.rb +0 -1
  19. data/lib/karafka/cli/info.rb +0 -1
  20. data/lib/karafka/cli/install.rb +0 -1
  21. data/lib/karafka/cli/server.rb +0 -1
  22. data/lib/karafka/cli/swarm.rb +0 -1
  23. data/lib/karafka/cli.rb +2 -2
  24. data/lib/karafka/connection/client.rb +44 -7
  25. data/lib/karafka/connection/listener.rb +3 -3
  26. data/lib/karafka/connection/manager.rb +0 -1
  27. data/lib/karafka/connection/pauses_manager.rb +20 -3
  28. data/lib/karafka/connection/proxy.rb +45 -0
  29. data/lib/karafka/connection/status.rb +0 -1
  30. data/lib/karafka/instrumentation/assignments_tracker.rb +34 -0
  31. data/lib/karafka/instrumentation/callbacks/consumer_groups/decorator.rb +19 -0
  32. data/lib/karafka/instrumentation/callbacks/consumer_groups/error.rb +0 -1
  33. data/lib/karafka/instrumentation/callbacks/consumer_groups/statistics.rb +3 -2
  34. data/lib/karafka/instrumentation/critical_errors_listener.rb +85 -0
  35. data/lib/karafka/instrumentation/logger_listener.rb +2 -0
  36. data/lib/karafka/instrumentation/monitor.rb +2 -3
  37. data/lib/karafka/instrumentation/vendors/appsignal/errors_listener.rb +0 -1
  38. data/lib/karafka/instrumentation/vendors/appsignal/metrics_listener.rb +0 -1
  39. data/lib/karafka/instrumentation/vendors/datadog/logger_listener.rb +4 -3
  40. data/lib/karafka/instrumentation/vendors/datadog/metrics_listener.rb +0 -2
  41. data/lib/karafka/instrumentation/vendors/kubernetes/liveness_listener.rb +181 -18
  42. data/lib/karafka/pro/admin/recovery/errors.rb +0 -1
  43. data/lib/karafka/pro/admin/recovery.rb +6 -4
  44. data/lib/karafka/pro/base_consumer.rb +5 -5
  45. data/lib/karafka/pro/cleaner/messages/metadata.rb +0 -1
  46. data/lib/karafka/pro/cli/parallel_segments/base.rb +1 -2
  47. data/lib/karafka/pro/connection/manager.rb +7 -4
  48. data/lib/karafka/pro/encryption/messages/middleware.rb +5 -0
  49. data/lib/karafka/pro/encryption/messages/parser.rb +4 -0
  50. data/lib/karafka/pro/instrumentation/callbacks/consumer_groups/decorator.rb +63 -0
  51. data/lib/karafka/pro/instrumentation/consumer_groups/lag_compensation/compensator.rb +120 -0
  52. data/lib/karafka/pro/instrumentation/consumer_groups/lag_compensation/fetcher.rb +88 -0
  53. data/lib/karafka/pro/instrumentation/consumer_groups/lag_compensation/refresher.rb +185 -0
  54. data/lib/karafka/pro/instrumentation/consumer_groups/lag_compensation/registry.rb +102 -0
  55. data/lib/karafka/pro/instrumentation/performance_tracker.rb +40 -6
  56. data/lib/karafka/pro/iterator/tpl_builder.rb +27 -7
  57. data/lib/karafka/pro/iterator.rb +22 -7
  58. data/lib/karafka/pro/loader.rb +12 -0
  59. data/lib/karafka/pro/processing/consumer_groups/adaptive_iterator/consumer.rb +0 -3
  60. data/lib/karafka/pro/processing/consumer_groups/adaptive_iterator/tracker.rb +2 -4
  61. data/lib/karafka/pro/processing/consumer_groups/collapser.rb +6 -9
  62. data/lib/karafka/pro/processing/consumer_groups/coordinator.rb +1 -4
  63. data/lib/karafka/pro/processing/consumer_groups/coordinators/errors_tracker.rb +0 -3
  64. data/lib/karafka/pro/processing/consumer_groups/coordinators/filters_applier.rb +21 -10
  65. data/lib/karafka/pro/processing/consumer_groups/coordinators/virtual_offset_manager.rb +4 -4
  66. data/lib/karafka/pro/processing/consumer_groups/expansions_selector.rb +0 -3
  67. data/lib/karafka/pro/processing/consumer_groups/filters/actions.rb +84 -0
  68. data/lib/karafka/pro/processing/consumer_groups/filters/base.rb +5 -6
  69. data/lib/karafka/pro/processing/consumer_groups/filters/delayer.rb +2 -5
  70. data/lib/karafka/pro/processing/consumer_groups/filters/expirer.rb +1 -4
  71. data/lib/karafka/pro/processing/consumer_groups/filters/inline_insights_delayer.rb +1 -4
  72. data/lib/karafka/pro/processing/consumer_groups/filters/throttler.rb +2 -5
  73. data/lib/karafka/pro/processing/consumer_groups/filters/virtual_limiter.rb +0 -3
  74. data/lib/karafka/pro/processing/consumer_groups/jobs/consume_non_blocking.rb +2 -3
  75. data/lib/karafka/pro/processing/consumer_groups/jobs/eofed_non_blocking.rb +0 -2
  76. data/lib/karafka/pro/processing/consumer_groups/jobs/periodic.rb +2 -3
  77. data/lib/karafka/pro/processing/consumer_groups/jobs/periodic_non_blocking.rb +0 -2
  78. data/lib/karafka/pro/processing/consumer_groups/jobs/revoked_non_blocking.rb +0 -3
  79. data/lib/karafka/pro/processing/consumer_groups/jobs_builder.rb +5 -4
  80. data/lib/karafka/pro/processing/consumer_groups/offset_metadata/consumer.rb +3 -7
  81. data/lib/karafka/pro/processing/consumer_groups/offset_metadata/fetcher.rb +8 -10
  82. data/lib/karafka/pro/processing/consumer_groups/offset_metadata/listener.rb +0 -3
  83. data/lib/karafka/pro/processing/consumer_groups/parallel_segments/filters/base.rb +3 -5
  84. data/lib/karafka/pro/processing/consumer_groups/parallel_segments/filters/default.rb +0 -3
  85. data/lib/karafka/pro/processing/consumer_groups/parallel_segments/filters/mom.rb +0 -3
  86. data/lib/karafka/pro/processing/consumer_groups/partitioner.rb +2 -5
  87. data/lib/karafka/pro/processing/consumer_groups/periodic_job/consumer.rb +2 -5
  88. data/lib/karafka/pro/processing/consumer_groups/piping/consumer.rb +2 -5
  89. data/lib/karafka/pro/processing/consumer_groups/strategies/aj/dlq_ftr_lrj_mom.rb +10 -9
  90. data/lib/karafka/pro/processing/consumer_groups/strategies/aj/dlq_ftr_lrj_mom_vp.rb +11 -10
  91. data/lib/karafka/pro/processing/consumer_groups/strategies/aj/dlq_ftr_mom.rb +0 -3
  92. data/lib/karafka/pro/processing/consumer_groups/strategies/aj/dlq_ftr_mom_vp.rb +0 -3
  93. data/lib/karafka/pro/processing/consumer_groups/strategies/aj/dlq_lrj_mom.rb +8 -7
  94. data/lib/karafka/pro/processing/consumer_groups/strategies/aj/dlq_lrj_mom_vp.rb +9 -8
  95. data/lib/karafka/pro/processing/consumer_groups/strategies/aj/dlq_mom.rb +5 -8
  96. data/lib/karafka/pro/processing/consumer_groups/strategies/aj/dlq_mom_vp.rb +3 -6
  97. data/lib/karafka/pro/processing/consumer_groups/strategies/aj/ftr_lrj_mom.rb +4 -7
  98. data/lib/karafka/pro/processing/consumer_groups/strategies/aj/ftr_lrj_mom_vp.rb +6 -9
  99. data/lib/karafka/pro/processing/consumer_groups/strategies/aj/ftr_mom.rb +3 -7
  100. data/lib/karafka/pro/processing/consumer_groups/strategies/aj/ftr_mom_vp.rb +4 -8
  101. data/lib/karafka/pro/processing/consumer_groups/strategies/aj/lrj_mom.rb +3 -6
  102. data/lib/karafka/pro/processing/consumer_groups/strategies/aj/lrj_mom_vp.rb +4 -7
  103. data/lib/karafka/pro/processing/consumer_groups/strategies/aj/mom.rb +2 -5
  104. data/lib/karafka/pro/processing/consumer_groups/strategies/aj/mom_vp.rb +3 -6
  105. data/lib/karafka/pro/processing/consumer_groups/strategies/base.rb +0 -3
  106. data/lib/karafka/pro/processing/consumer_groups/strategies/default.rb +56 -33
  107. data/lib/karafka/pro/processing/consumer_groups/strategies/dlq/default.rb +27 -14
  108. data/lib/karafka/pro/processing/consumer_groups/strategies/dlq/ftr.rb +0 -3
  109. data/lib/karafka/pro/processing/consumer_groups/strategies/dlq/ftr_lrj.rb +4 -7
  110. data/lib/karafka/pro/processing/consumer_groups/strategies/dlq/ftr_lrj_mom.rb +5 -8
  111. data/lib/karafka/pro/processing/consumer_groups/strategies/dlq/ftr_lrj_mom_vp.rb +5 -8
  112. data/lib/karafka/pro/processing/consumer_groups/strategies/dlq/ftr_lrj_vp.rb +3 -7
  113. data/lib/karafka/pro/processing/consumer_groups/strategies/dlq/ftr_mom.rb +0 -3
  114. data/lib/karafka/pro/processing/consumer_groups/strategies/dlq/ftr_mom_vp.rb +0 -3
  115. data/lib/karafka/pro/processing/consumer_groups/strategies/dlq/ftr_vp.rb +0 -3
  116. data/lib/karafka/pro/processing/consumer_groups/strategies/dlq/lrj.rb +2 -5
  117. data/lib/karafka/pro/processing/consumer_groups/strategies/dlq/lrj_mom.rb +0 -3
  118. data/lib/karafka/pro/processing/consumer_groups/strategies/dlq/lrj_mom_vp.rb +0 -3
  119. data/lib/karafka/pro/processing/consumer_groups/strategies/dlq/lrj_vp.rb +3 -6
  120. data/lib/karafka/pro/processing/consumer_groups/strategies/dlq/mom.rb +0 -3
  121. data/lib/karafka/pro/processing/consumer_groups/strategies/dlq/mom_vp.rb +3 -6
  122. data/lib/karafka/pro/processing/consumer_groups/strategies/dlq/vp.rb +65 -8
  123. data/lib/karafka/pro/processing/consumer_groups/strategies/ftr/default.rb +0 -4
  124. data/lib/karafka/pro/processing/consumer_groups/strategies/ftr/vp.rb +2 -5
  125. data/lib/karafka/pro/processing/consumer_groups/strategies/lrj/default.rb +0 -3
  126. data/lib/karafka/pro/processing/consumer_groups/strategies/lrj/ftr.rb +3 -6
  127. data/lib/karafka/pro/processing/consumer_groups/strategies/lrj/ftr_mom.rb +4 -7
  128. data/lib/karafka/pro/processing/consumer_groups/strategies/lrj/ftr_mom_vp.rb +4 -8
  129. data/lib/karafka/pro/processing/consumer_groups/strategies/lrj/ftr_vp.rb +3 -6
  130. data/lib/karafka/pro/processing/consumer_groups/strategies/lrj/mom.rb +2 -5
  131. data/lib/karafka/pro/processing/consumer_groups/strategies/lrj/mom_vp.rb +3 -7
  132. data/lib/karafka/pro/processing/consumer_groups/strategies/lrj/vp.rb +2 -5
  133. data/lib/karafka/pro/processing/consumer_groups/strategies/mom/default.rb +0 -3
  134. data/lib/karafka/pro/processing/consumer_groups/strategies/mom/ftr.rb +0 -3
  135. data/lib/karafka/pro/processing/consumer_groups/strategies/mom/ftr_vp.rb +0 -3
  136. data/lib/karafka/pro/processing/consumer_groups/strategies/mom/vp.rb +0 -3
  137. data/lib/karafka/pro/processing/consumer_groups/strategies/vp/default.rb +11 -6
  138. data/lib/karafka/pro/processing/consumer_groups/strategy_selector.rb +0 -3
  139. data/lib/karafka/pro/processing/consumer_groups/subscription_groups_coordinator.rb +0 -3
  140. data/lib/karafka/pro/processing/consumer_groups/virtual_partitions/distributors/balanced.rb +0 -3
  141. data/lib/karafka/pro/processing/consumer_groups/virtual_partitions/distributors/base.rb +2 -4
  142. data/lib/karafka/pro/processing/consumer_groups/virtual_partitions/distributors/consistent.rb +0 -3
  143. data/lib/karafka/pro/processing/jobs_queue.rb +4 -1
  144. data/lib/karafka/pro/processing/schedulers/base.rb +4 -2
  145. data/lib/karafka/pro/processing/schedulers/default.rb +2 -2
  146. data/lib/karafka/pro/recurring_tasks/consumer.rb +9 -2
  147. data/lib/karafka/pro/recurring_tasks/contracts/config.rb +0 -1
  148. data/lib/karafka/pro/recurring_tasks/executor.rb +25 -13
  149. data/lib/karafka/pro/routing/features/consumer_groups/direct_assignments/subscription_group.rb +0 -2
  150. data/lib/karafka/pro/routing/features/consumer_groups/long_running_job.rb +0 -2
  151. data/lib/karafka/pro/routing/features/consumer_groups/multiplexing/proxy.rb +2 -2
  152. data/lib/karafka/pro/routing/features/consumer_groups/non_blocking_job.rb +0 -2
  153. data/lib/karafka/pro/routing/features/consumer_groups/offset_metadata/topic.rb +0 -6
  154. data/lib/karafka/pro/routing/features/consumer_groups/parallel_segments/consumer_group.rb +5 -4
  155. data/lib/karafka/pro/routing/features/consumer_groups/patterns/config.rb +0 -6
  156. data/lib/karafka/pro/routing/features/consumer_groups/patterns/consumer_group.rb +2 -1
  157. data/lib/karafka/pro/routing/features/consumer_groups/patterns/contracts/pattern.rb +0 -1
  158. data/lib/karafka/pro/routing/features/consumer_groups/patterns/detector.rb +2 -1
  159. data/lib/karafka/pro/routing/features/consumer_groups/patterns/pattern.rb +2 -1
  160. data/lib/karafka/pro/routing/features/consumer_groups/patterns/topics.rb +8 -8
  161. data/lib/karafka/pro/routing/features/consumer_groups/periodic_job/topic.rb +5 -5
  162. data/lib/karafka/pro/routing/features/consumer_groups/recurring_tasks/builder.rb +34 -15
  163. data/lib/karafka/pro/routing/features/consumer_groups/scheduled_messages/builder.rb +52 -28
  164. data/lib/karafka/pro/routing/features/consumer_groups/scheduled_messages/topic.rb +2 -1
  165. data/lib/karafka/pro/routing/features/consumer_groups/virtual_partitions/topic.rb +6 -5
  166. data/lib/karafka/pro/routing/features/pausing/config.rb +7 -11
  167. data/lib/karafka/pro/routing/features/pausing/contracts/topic.rb +6 -8
  168. data/lib/karafka/pro/routing/features/pausing/topic.rb +18 -63
  169. data/lib/karafka/pro/scheduled_messages/consumer.rb +7 -1
  170. data/lib/karafka/pro/scheduled_messages/schema_validator.rb +6 -1
  171. data/lib/karafka/pro/swarm/liveness_listener.rb +118 -13
  172. data/lib/karafka/pro.rb +0 -6
  173. data/lib/karafka/processing/consumer_groups/coordinator.rb +4 -5
  174. data/lib/karafka/processing/consumer_groups/coordinators_buffer.rb +6 -2
  175. data/lib/karafka/processing/consumer_groups/executor.rb +4 -4
  176. data/lib/karafka/processing/consumer_groups/executors_buffer.rb +2 -4
  177. data/lib/karafka/processing/consumer_groups/expansions_selector.rb +0 -2
  178. data/lib/karafka/processing/consumer_groups/inline_insights/consumer.rb +0 -2
  179. data/lib/karafka/processing/consumer_groups/inline_insights/listener.rb +0 -2
  180. data/lib/karafka/processing/consumer_groups/inline_insights/tracker.rb +14 -16
  181. data/lib/karafka/processing/consumer_groups/jobs/consume.rb +2 -5
  182. data/lib/karafka/processing/consumer_groups/jobs/eofed.rb +2 -3
  183. data/lib/karafka/processing/consumer_groups/jobs/idle.rb +2 -2
  184. data/lib/karafka/processing/consumer_groups/jobs/revoked.rb +2 -4
  185. data/lib/karafka/processing/consumer_groups/jobs/shutdown.rb +2 -2
  186. data/lib/karafka/processing/consumer_groups/partitioner.rb +3 -6
  187. data/lib/karafka/processing/consumer_groups/strategies/aj_dlq_mom.rb +5 -2
  188. data/lib/karafka/processing/consumer_groups/strategies/aj_mom.rb +2 -4
  189. data/lib/karafka/processing/consumer_groups/strategies/base.rb +3 -5
  190. data/lib/karafka/processing/consumer_groups/strategies/default.rb +43 -13
  191. data/lib/karafka/processing/consumer_groups/strategies/dlq.rb +5 -2
  192. data/lib/karafka/processing/consumer_groups/strategies/dlq_mom.rb +5 -2
  193. data/lib/karafka/processing/consumer_groups/strategies/mom.rb +2 -3
  194. data/lib/karafka/processing/consumer_groups/strategy_selector.rb +0 -2
  195. data/lib/karafka/processing/coordinator.rb +0 -2
  196. data/lib/karafka/processing/coordinators_buffer.rb +0 -2
  197. data/lib/karafka/processing/partitioner.rb +0 -2
  198. data/lib/karafka/processing/strategy_selector.rb +0 -2
  199. data/lib/karafka/processing/worker.rb +14 -6
  200. data/lib/karafka/processing/workers_pool.rb +21 -4
  201. data/lib/karafka/railtie.rb +0 -1
  202. data/lib/karafka/routing/contracts/routing.rb +2 -2
  203. data/lib/karafka/routing/features/consumer_groups/active_job/topic.rb +6 -5
  204. data/lib/karafka/routing/features/consumer_groups/manual_offset_management/topic.rb +2 -2
  205. data/lib/karafka/routing/features/deserializers/topic.rb +0 -1
  206. data/lib/karafka/routing/features/pausing/config.rb +27 -0
  207. data/lib/karafka/routing/features/pausing.rb +13 -0
  208. data/lib/karafka/routing/subscription_group.rb +1 -1
  209. data/lib/karafka/routing/topic.rb +20 -7
  210. data/lib/karafka/routing/topics.rb +21 -1
  211. data/lib/karafka/server.rb +9 -0
  212. data/lib/karafka/setup/config.rb +52 -55
  213. data/lib/karafka/setup/config_proxy.rb +2 -1
  214. data/lib/karafka/setup/contracts/config.rb +14 -0
  215. data/lib/karafka/swarm/node.rb +2 -2
  216. data/lib/karafka/version.rb +1 -2
  217. data/lib/karafka.rb +2 -2
  218. metadata +14 -4
@@ -50,12 +50,13 @@ module Karafka
50
50
  # groups that will be created)
51
51
  # @param partitioner [nil, #call] nil or callable partitioner
52
52
  # @param reducer [nil, #call] reducer for parallel key. It allows for using a custom
53
- # reducer to achieve enhanced parallelization when the default reducer is not enough.
53
+ # reducer to achieve enhanced parallelization when the default reducer is not
54
+ # enough.
54
55
  # @param merge_key [String] key used to build the parallel segment consumer groups
55
56
  #
56
- # @note This method is an assignor but the API is actually via the `#parallel_segments`
57
- # method. Our `Routing::Proxy` normalizes that the way we want to have it exposed
58
- # for the end users.
57
+ # @note This method is an assignor but the API is actually via the
58
+ # `#parallel_segments` method. Our `Routing::Proxy` normalizes that the way we want
59
+ # to have it exposed for the end users.
59
60
  def parallel_segments=(
60
61
  count: 1,
61
62
  partitioner: nil,
@@ -33,12 +33,6 @@ module Karafka
33
33
  module Routing
34
34
  module Features
35
35
  module ConsumerGroups
36
- # Holds pattern info reference
37
- # Type is set to:
38
- # `:regular` - in case patterns are not used and topic is just a regular existing topic
39
- # matched directly based on the name
40
- # `:discovered` - in case it is a real topic on which we started to listed
41
- # `:matcher` - represents a regular expression used by librdkafka
42
36
  class Patterns < Base
43
37
  # Config for pattern based topic Only pattern related topics are active in this context
44
38
  Config = Struct.new(
@@ -42,7 +42,8 @@ module Karafka
42
42
  @patterns = Patterns.new([])
43
43
  end
44
44
 
45
- # @return [::Karafka::Pro::Routing::Features::ConsumerGroups::Patterns::Patterns] created patterns
45
+ # @return [::Karafka::Pro::Routing::Features::ConsumerGroups::Patterns::Patterns]
46
+ # created patterns
46
47
  def patterns
47
48
  @patterns
48
49
  end
@@ -34,7 +34,6 @@ module Karafka
34
34
  module Features
35
35
  module ConsumerGroups
36
36
  class Patterns < Base
37
- # Namespace for patterns related contracts
38
37
  module Contracts
39
38
  # Contract used to validate pattern data
40
39
  class Pattern < Karafka::Contracts::Base
@@ -71,7 +71,8 @@ module Karafka
71
71
 
72
72
  # Adds the discovered topic into the routing
73
73
  #
74
- # @param pattern [Karafka::Pro::Routing::Features::ConsumerGroups::Patterns::Pattern] matched pattern
74
+ # @param pattern [Karafka::Pro::Routing::Features::ConsumerGroups::Patterns::Pattern]
75
+ # matched pattern
75
76
  # @param discovered_topic [String] topic that we discovered that should be part of the
76
77
  # routing from now on.
77
78
  # @param sg_topics [Array<Karafka::Routing::Topic>]
@@ -100,7 +100,8 @@ module Karafka
100
100
  # prevent it from running but internally in this component we need to ensure, that
101
101
  # prior to the validations we operate on a regexp
102
102
  #
103
- # @return [Regexp] returns a regexp always even if what we've received was not a regexp
103
+ # @return [Regexp] returns a regexp always even if what we've received was not a
104
+ # regexp
104
105
  def safe_regexp
105
106
  # This regexp will never match anything
106
107
  regexp.is_a?(Regexp) ? regexp : /$a/
@@ -47,15 +47,15 @@ module Karafka
47
47
  #
48
48
  # @param topic_name [String] topic name
49
49
  # @return [Karafka::Routing::Topic]
50
- # @raise [Karafka::Errors::TopicNotFoundError] this should never happen. If you see it,
51
- # please create an issue.
50
+ # @raise [Karafka::Errors::TopicNotFoundError] this should never happen. If you see
51
+ # it, please create an issue.
52
52
  #
53
- # @note This method should not be used in context of finding multiple missing topics in
54
- # loops because it catches exceptions and attempts to expand routes. If this is used
55
- # in a loop for lookups on thousands of topics with detector expansion, this may
56
- # be slow. It should be used in the context where newly discovered topics are found
57
- # and should by design match a pattern. For quick lookups on batches of topics, it
58
- # is recommended to use a custom built lookup with conditional expander.
53
+ # @note This method should not be used in context of finding multiple missing topics
54
+ # in loops because it catches exceptions and attempts to expand routes. If this is
55
+ # used in a loop for lookups on thousands of topics with detector expansion, this
56
+ # may be slow. It should be used in the context where newly discovered topics are
57
+ # found and should by design match a pattern. For quick lookups on batches of
58
+ # topics, it is recommended to use a custom built lookup with conditional expander.
59
59
  def find(topic_name)
60
60
  super
61
61
  rescue Karafka::Errors::TopicNotFoundError
@@ -49,12 +49,12 @@ module Karafka
49
49
  # poll where messages were not received.
50
50
  # @param active [Boolean] should ticking happen for this topic assignments.
51
51
  # @param interval [Integer] minimum interval to run periodic jobs on given topic.
52
- # @param during_pause [Boolean, nil] Should periodic jobs run when partition is paused.
53
- # It is set to `nil` by default allowing for detection when this value is not
54
- # configured but should be built dynamically based on LRJ status.
52
+ # @param during_pause [Boolean, nil] Should periodic jobs run when partition is
53
+ # paused. It is set to `nil` by default allowing for detection when this value is
54
+ # not configured but should be built dynamically based on LRJ status.
55
55
  # @param during_retry [Boolean, nil] Should we run when there was an error and we are
56
- # in a retry flow. Please note that for this to work, `during_pause` also needs to be
57
- # set to true as errors retry happens after pause.
56
+ # in a retry flow. Please note that for this to work, `during_pause` also needs to
57
+ # be set to true as errors retry happens after pause.
58
58
  def periodic_job(
59
59
  active = false,
60
60
  interval: nil,
@@ -52,6 +52,39 @@ module Karafka
52
52
  tasks_cfg = App.config.recurring_tasks
53
53
  topics_cfg = tasks_cfg.topics
54
54
 
55
+ # Declarative topic definitions live independently of routing. We declare the
56
+ # broker-side structure here so it is managed by the CLI topics commands without
57
+ # relying on the (to be retired) routing `config(...)` bridge.
58
+ #
59
+ # We guard each declaration with `find_topic` because `Declaratives::Builder#topic`
60
+ # re-applies its block on an already existing declaration (additive semantics),
61
+ # unlike the old bridge which used `||=` and kept the first declaration. Without the
62
+ # guard, our framework defaults would clobber a topic the user may have declared
63
+ # themselves via `App.declaratives.draw`. Skipping when already present preserves
64
+ # first-declaration-wins.
65
+ App.declaratives.draw do
66
+ # Keep older data for a day and compact to the last state available
67
+ unless find_topic(topics_cfg.schedules.name)
68
+ topic(topics_cfg.schedules.name) do
69
+ config(
70
+ "cleanup.policy": "compact,delete",
71
+ "retention.ms": 86_400_000
72
+ )
73
+ end
74
+ end
75
+
76
+ # Keep cron logs of executions for a week and after that remove. Week should be
77
+ # enough and should not produce too much data.
78
+ unless find_topic(topics_cfg.logs.name)
79
+ topic(topics_cfg.logs.name) do
80
+ config(
81
+ "cleanup.policy": "delete",
82
+ "retention.ms": 604_800_000
83
+ )
84
+ end
85
+ end
86
+ end
87
+
55
88
  consumer_group tasks_cfg.group_id do
56
89
  # Registers the primary topic that we use to control schedules execution. This is
57
90
  # the one that we use to trigger recurring tasks.
@@ -84,14 +117,7 @@ module Karafka
84
117
  # Since the execution of particular tasks is isolated and guarded, it should not
85
118
  # leak. This means, that this is to handle errors like schedule version
86
119
  # incompatibility and other errors that will not go away without a redeployment
87
- pause_timeout(60 * 1_000)
88
- pause_max_timeout(60 * 1_000)
89
-
90
- # Keep older data for a day and compact to the last state available
91
- config(
92
- "cleanup.policy": "compact,delete",
93
- "retention.ms": 86_400_000
94
- )
120
+ pause(timeout: 60 * 1_000, max_timeout: 60 * 1_000)
95
121
 
96
122
  # This is the core of execution. Since we're producers of states, we need a way
97
123
  # to tick without having new data
@@ -113,13 +139,6 @@ module Karafka
113
139
  active(false)
114
140
  deserializer tasks_cfg.deserializer
115
141
  target.recurring_tasks(true)
116
-
117
- # Keep cron logs of executions for a week and after that remove. Week should be
118
- # enough and should not produce too much data.
119
- config(
120
- "cleanup.policy": "delete",
121
- "retention.ms": 604_800_000
122
- )
123
142
  end
124
143
 
125
144
  yield(schedules_topic, logs_topic) if block && block.arity.positive?
@@ -38,9 +38,9 @@ module Karafka
38
38
  module Builder
39
39
  # Enabled scheduled messages operations and adds needed topics and other stuff.
40
40
  #
41
- # @param topic_name [String, false] name for scheduled messages topic that is also used
42
- # as a group identifier. Users can have multiple schedule topics flows to prevent key
43
- # collisions, prioritize and do other stuff. `false` if not active.
41
+ # @param topic_name [String, false] name for scheduled messages topic that is also
42
+ # used as a group identifier. Users can have multiple schedule topics flows to
43
+ # prevent key collisions, prioritize and do other stuff. `false` if not active.
44
44
  # @param block [Proc] optional reconfiguration of the topics definitions.
45
45
  # @note Namespace for topics should include the divider as it is not automatically
46
46
  # added.
@@ -54,6 +54,54 @@ module Karafka
54
54
  # should have same partition count.
55
55
  default_partitions = 5
56
56
  msg_cfg = App.config.scheduled_messages
57
+ states_topic_name = "#{topic_name}#{msg_cfg.states_postfix}"
58
+
59
+ # Declarative topic definitions live independently of routing. We declare the
60
+ # broker-side structure here so it is managed by the CLI topics commands without
61
+ # relying on the (to be retired) routing `config(...)` bridge.
62
+ #
63
+ # We guard each declaration with `find_topic` because `Declaratives::Builder#topic`
64
+ # re-applies its block on an already existing declaration (additive semantics),
65
+ # unlike the old bridge which used `||=` and kept the first declaration. Without the
66
+ # guard, our framework defaults would clobber a topic the user may have declared
67
+ # themselves via `App.declaratives.draw`. Skipping when already present preserves
68
+ # first-declaration-wins.
69
+ App.declaratives.draw do
70
+ # This is a setup that should allow messages to be compacted fairly fast. Since
71
+ # each dispatched message should be removed via tombstone, they do not have to
72
+ # be present in the topic for too long.
73
+ unless find_topic(topic_name)
74
+ topic(topic_name) do
75
+ partitions default_partitions
76
+ config(
77
+ # Will ensure, that after tombstone is present, given scheduled message,
78
+ # that has been dispatched is removed by Kafka
79
+ "cleanup.policy": "compact",
80
+ # When 10% or more dispatches are done, compact data
81
+ "min.cleanable.dirty.ratio": 0.1,
82
+ # Frequent segment rotation to support intense compaction
83
+ "segment.ms": 3_600_000,
84
+ "delete.retention.ms": 3_600_000,
85
+ "segment.bytes": 52_428_800
86
+ )
87
+ end
88
+ end
89
+
90
+ # Holds states of scheduler per each of the partitions. Same partition count as
91
+ # the messages topic since they tick independently per partition.
92
+ unless find_topic(states_topic_name)
93
+ topic(states_topic_name) do
94
+ partitions default_partitions
95
+ config(
96
+ "cleanup.policy": "compact",
97
+ "min.cleanable.dirty.ratio": 0.1,
98
+ "segment.ms": 3_600_000,
99
+ "delete.retention.ms": 3_600_000,
100
+ "segment.bytes": 52_428_800
101
+ )
102
+ end
103
+ end
104
+ end
57
105
 
58
106
  consumer_group msg_cfg.group_id do
59
107
  # Registers the primary topic that we use to control schedules execution. This is
@@ -93,22 +141,6 @@ module Karafka
93
141
 
94
142
  max_wait_time(1_000)
95
143
 
96
- # This is a setup that should allow messages to be compacted fairly fast. Since
97
- # each dispatched message should be removed via tombstone, they do not have to
98
- # be present in the topic for too long.
99
- config(
100
- partitions: default_partitions,
101
- # Will ensure, that after tombstone is present, given scheduled message, that
102
- # has been dispatched is removed by Kafka
103
- "cleanup.policy": "compact",
104
- # When 10% or more dispatches are done, compact data
105
- "min.cleanable.dirty.ratio": 0.1,
106
- # Frequent segment rotation to support intense compaction
107
- "segment.ms": 3_600_000,
108
- "delete.retention.ms": 3_600_000,
109
- "segment.bytes": 52_428_800
110
- )
111
-
112
144
  # This is the core of execution. Since we dispatch data in time intervals, we
113
145
  # need to be able to do this even when no new data is coming
114
146
  periodic(
@@ -126,17 +158,9 @@ module Karafka
126
158
  # Holds states of scheduler per each of the partitions since they tick
127
159
  # independently. We only hold future statistics not to have to deal with
128
160
  # any type of state restoration
129
- states_topic = topic("#{topic_name}#{msg_cfg.states_postfix}") do
161
+ states_topic = topic(states_topic_name) do
130
162
  active(false)
131
163
  target.scheduled_messages(true)
132
- config(
133
- partitions: default_partitions,
134
- "cleanup.policy": "compact",
135
- "min.cleanable.dirty.ratio": 0.1,
136
- "segment.ms": 3_600_000,
137
- "delete.retention.ms": 3_600_000,
138
- "segment.bytes": 52_428_800
139
- )
140
164
  deserializers(
141
165
  payload: msg_cfg.deserializers.payload
142
166
  )
@@ -46,7 +46,8 @@ module Karafka
46
46
  super
47
47
  end
48
48
 
49
- # @param active [Boolean] should this topic be considered related to scheduled messages
49
+ # @param active [Boolean] should this topic be considered related to scheduled
50
+ # messages
50
51
  def scheduled_messages(active = false)
51
52
  @scheduled_messages ||= Config.new(active: active)
52
53
  end
@@ -45,16 +45,17 @@ module Karafka
45
45
  super
46
46
  end
47
47
 
48
- # @param max_partitions [Integer] max number of virtual partitions that can come out of
49
- # the single distribution flow. When set to more than the Karafka threading, will
48
+ # @param max_partitions [Integer] max number of virtual partitions that can come out
49
+ # of the single distribution flow. When set to more than the Karafka threading, will
50
50
  # create more work than workers. When less, can ensure we have spare resources to
51
51
  # process other things in parallel.
52
52
  # @param partitioner [nil, #call] nil or callable partitioner
53
53
  # @param offset_metadata_strategy [Symbol] how we should match the metadata for the
54
- # offset. `:exact` will match the offset matching metadata and `:current` will select
55
- # the most recently reported metadata
54
+ # offset. `:exact` will match the offset matching metadata and `:current` will
55
+ # select the most recently reported metadata
56
56
  # @param reducer [nil, #call] reducer for VPs key. It allows for using a custom
57
- # reducer to achieve enhanced parallelization when the default reducer is not enough.
57
+ # reducer to achieve enhanced parallelization when the default reducer is not
58
+ # enough.
58
59
  # @param distribution [Symbol] the strategy to use for virtual partitioning. Can be
59
60
  # either `:consistent` or `:balanced`. The `:balanced` strategy ensures balanced
60
61
  # distribution of work across available workers while maintaining message order
@@ -33,17 +33,13 @@ module Karafka
33
33
  module Routing
34
34
  module Features
35
35
  class Pausing < Base
36
- # Config for pausing feature
37
- Config = Struct.new(
38
- :active,
39
- :timeout,
40
- :max_timeout,
41
- :with_exponential_backoff,
42
- keyword_init: true
43
- ) do
44
- alias_method :active?, :active
45
- alias_method :with_exponential_backoff?, :with_exponential_backoff
46
- end
36
+ # Config for pausing feature.
37
+ #
38
+ # By convention each routing feature exposes a `Config`. The pause value object is defined
39
+ # in OSS (`Karafka::Routing::Features::Pausing::Config`) because the per-topic `#pause`
40
+ # reader defaults to the global config there, so the feature points its `Config` at that
41
+ # shared class rather than redefining it.
42
+ Config = Karafka::Routing::Features::Pausing::Config
47
43
  end
48
44
  end
49
45
  end
@@ -42,10 +42,8 @@ module Karafka
42
42
  File.join(Karafka.gem_root, "config", "locales", "pro_errors.yml")
43
43
  ).fetch("en").fetch("validations").fetch("routing").fetch("topic")
44
44
 
45
- # Validate the nested pausing configuration
46
- # Both old setters and new pause() method update the pausing config,
47
- # so we only need to validate this one format
48
- nested :pausing do
45
+ # Validate the nested pause configuration
46
+ nested :pause do
49
47
  required(:active) { |val| [true, false].include?(val) }
50
48
  required(:timeout) { |val| val.is_a?(Integer) && val.positive? }
51
49
  required(:max_timeout) { |val| val.is_a?(Integer) && val.positive? }
@@ -56,13 +54,13 @@ module Karafka
56
54
  virtual do |data, errors|
57
55
  next unless errors.empty?
58
56
 
59
- pausing = data.fetch(:pausing)
60
- timeout = pausing.fetch(:timeout)
61
- max_timeout = pausing.fetch(:max_timeout)
57
+ pause = data.fetch(:pause)
58
+ timeout = pause.fetch(:timeout)
59
+ max_timeout = pause.fetch(:max_timeout)
62
60
 
63
61
  next if timeout <= max_timeout
64
62
 
65
- [[%i[pausing timeout], :max_timeout_vs_pause_max_timeout]]
63
+ [[%i[pause timeout], :max_timeout_vs_pause_max_timeout]]
66
64
  end
67
65
  end
68
66
  end
@@ -35,84 +35,39 @@ module Karafka
35
35
  class Pausing < Base
36
36
  # Expansion allowing for a per topic pause strategy definitions
37
37
  module Topic
38
- # This method sets up the extra instance variable to nil before calling
39
- # the parent class initializer. The explicit initialization
40
- # to nil is included as an optimization for Ruby's object shapes system,
41
- # which improves memory layout and access performance.
42
- def initialize(...)
43
- @pausing = nil
44
- super
45
- end
46
-
47
- # Allows for per-topic pausing strategy setting
38
+ # Allows for per-topic pausing strategy setting.
39
+ #
40
+ # Overrides the OSS `#pause` reader (this module is prepended onto `Routing::Topic`).
41
+ # With no arguments it returns the current configuration, defaulting to the global
42
+ # `config.pause.*` settings via `super`. With arguments it overrides the pausing
43
+ # strategy for this topic and marks it as active.
48
44
  #
49
45
  # @param timeout [Integer] how long should we wait upon processing error (milliseconds)
50
46
  # @param max_timeout [Integer] what is the max timeout in case of an exponential
51
47
  # backoff (milliseconds)
52
48
  # @param with_exponential_backoff [Boolean] should we use exponential backoff
53
- # @return [Config] pausing config object
49
+ # @return [Karafka::Routing::Features::Pausing::Config] pausing config object
54
50
  def pause(timeout: nil, max_timeout: nil, with_exponential_backoff: nil)
55
- # If no arguments provided, just return or initialize the config
56
- return pausing if timeout.nil? && max_timeout.nil? && with_exponential_backoff.nil?
51
+ config = super()
57
52
 
58
- # Update instance variables for backwards compatibility
59
- # This ensures code reading @pause_timeout directly or via the inherited getter
60
- # will get the correct values
61
- @pause_timeout = timeout if timeout
62
- @pause_max_timeout = max_timeout if max_timeout
63
-
64
- unless with_exponential_backoff.nil?
65
- @pause_with_exponential_backoff = with_exponential_backoff
66
- end
53
+ # If no arguments provided, just return the current (default or overridden) config
54
+ return config if timeout.nil? && max_timeout.nil? && with_exponential_backoff.nil?
67
55
 
68
- # Create or update the config
69
- @pausing ||= Config.new(
70
- active: false,
71
- timeout: @pause_timeout || Karafka::App.config.pause.timeout,
72
- max_timeout: @pause_max_timeout || Karafka::App.config.pause.max_timeout,
73
- with_exponential_backoff: if @pause_with_exponential_backoff.nil?
74
- Karafka::App.config.pause.with_exponential_backoff
75
- else
76
- @pause_with_exponential_backoff
77
- end
78
- )
79
-
80
- @pausing.timeout = timeout if timeout
81
- @pausing.max_timeout = max_timeout if max_timeout
56
+ config.timeout = timeout if timeout
57
+ config.max_timeout = max_timeout if max_timeout
82
58
 
83
59
  unless with_exponential_backoff.nil?
84
- @pausing.with_exponential_backoff = with_exponential_backoff
60
+ config.with_exponential_backoff = with_exponential_backoff
85
61
  end
86
62
 
87
- @pausing.active = true
88
-
89
- @pausing
90
- end
91
-
92
- # @return [Config] pausing configuration object
93
- def pausing
94
- @pausing ||= Config.new(
95
- active: false,
96
- timeout: @pause_timeout || Karafka::App.config.pause.timeout,
97
- max_timeout: @pause_max_timeout || Karafka::App.config.pause.max_timeout,
98
- with_exponential_backoff: if @pause_with_exponential_backoff.nil?
99
- Karafka::App.config.pause.with_exponential_backoff
100
- else
101
- @pause_with_exponential_backoff
102
- end
103
- )
104
- end
63
+ config.active = true
105
64
 
106
- # @return [Boolean] is pausing explicitly configured
107
- def pausing?
108
- pausing.active?
65
+ config
109
66
  end
110
67
 
111
- # @return [Hash] topic with all its native configuration options plus pausing settings
112
- def to_h
113
- super.merge(
114
- pausing: pausing.to_h
115
- ).freeze
68
+ # @return [Boolean] is pausing explicitly configured on a per-topic basis
69
+ def pause?
70
+ pause.active?
116
71
  end
117
72
  end
118
73
  end
@@ -72,7 +72,13 @@ module Karafka
72
72
 
73
73
  @states_reporter.call
74
74
 
75
- recent_timestamp = messages.last.timestamp.to_i
75
+ # Clamp to the wall clock: a future-dated message timestamp (producer clock skew or an
76
+ # explicitly set future timestamp) must not be treated as proof of catch-up. Otherwise a
77
+ # single future-dated message read mid-backlog would flip `loaded!` while older tombstones
78
+ # at higher offsets are still unread, dispatching schedules whose cancelling tombstones
79
+ # have not been loaded yet (double dispatch). Mirrors the `[timestamp, now].min` clamp in
80
+ # the batch metadata builder.
81
+ recent_timestamp = [messages.last.timestamp.to_i, Time.now.to_i].min
76
82
  post_started_timestamp = @tracker.started_at + GRACE_PERIOD
77
83
 
78
84
  # If we started getting messages that are beyond the current time, it means we have
@@ -43,7 +43,12 @@ module Karafka
43
43
  def call(message)
44
44
  message_version = message.headers["schedule_schema_version"]
45
45
 
46
- return if message_version <= ScheduledMessages::SCHEMA_VERSION
46
+ # Versions are semver, so they need to be compared segment-wise and not as strings
47
+ # ("1.2.0" is older than "1.10.0" but lexicographically greater)
48
+ message_gem_version = Gem::Version.new(message_version)
49
+ current_gem_version = Gem::Version.new(ScheduledMessages::SCHEMA_VERSION)
50
+
51
+ return if message_gem_version <= current_gem_version
47
52
 
48
53
  raise Errors::IncompatibleSchemaError, message_version
49
54
  end