karafka 1.4.13 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +3 -3
  3. data/.github/workflows/ci.yml +85 -30
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +268 -7
  6. data/CONTRIBUTING.md +10 -19
  7. data/Gemfile +6 -0
  8. data/Gemfile.lock +44 -87
  9. data/LICENSE +17 -0
  10. data/LICENSE-COMM +89 -0
  11. data/LICENSE-LGPL +165 -0
  12. data/README.md +44 -48
  13. data/bin/benchmarks +85 -0
  14. data/bin/create_token +22 -0
  15. data/bin/integrations +237 -0
  16. data/bin/karafka +4 -0
  17. data/bin/scenario +29 -0
  18. data/bin/stress_many +13 -0
  19. data/bin/stress_one +13 -0
  20. data/bin/wait_for_kafka +20 -0
  21. data/certs/karafka-pro.pem +11 -0
  22. data/config/errors.yml +55 -40
  23. data/docker-compose.yml +39 -3
  24. data/karafka.gemspec +11 -17
  25. data/lib/active_job/karafka.rb +21 -0
  26. data/lib/active_job/queue_adapters/karafka_adapter.rb +26 -0
  27. data/lib/karafka/active_job/consumer.rb +26 -0
  28. data/lib/karafka/active_job/dispatcher.rb +38 -0
  29. data/lib/karafka/active_job/job_extensions.rb +34 -0
  30. data/lib/karafka/active_job/job_options_contract.rb +21 -0
  31. data/lib/karafka/active_job/routing/extensions.rb +31 -0
  32. data/lib/karafka/app.rb +15 -20
  33. data/lib/karafka/base_consumer.rb +181 -31
  34. data/lib/karafka/cli/base.rb +4 -4
  35. data/lib/karafka/cli/info.rb +43 -9
  36. data/lib/karafka/cli/install.rb +19 -10
  37. data/lib/karafka/cli/server.rb +17 -42
  38. data/lib/karafka/cli.rb +4 -11
  39. data/lib/karafka/connection/client.rb +385 -90
  40. data/lib/karafka/connection/listener.rb +246 -38
  41. data/lib/karafka/connection/listeners_batch.rb +24 -0
  42. data/lib/karafka/connection/messages_buffer.rb +84 -0
  43. data/lib/karafka/connection/pauses_manager.rb +46 -0
  44. data/lib/karafka/connection/raw_messages_buffer.rb +101 -0
  45. data/lib/karafka/connection/rebalance_manager.rb +78 -0
  46. data/lib/karafka/contracts/base.rb +17 -0
  47. data/lib/karafka/contracts/config.rb +88 -11
  48. data/lib/karafka/contracts/consumer_group.rb +21 -189
  49. data/lib/karafka/contracts/consumer_group_topic.rb +34 -11
  50. data/lib/karafka/contracts/server_cli_options.rb +19 -18
  51. data/lib/karafka/contracts.rb +1 -1
  52. data/lib/karafka/env.rb +46 -0
  53. data/lib/karafka/errors.rb +21 -21
  54. data/lib/karafka/helpers/async.rb +33 -0
  55. data/lib/karafka/helpers/colorize.rb +20 -0
  56. data/lib/karafka/helpers/multi_delegator.rb +2 -2
  57. data/lib/karafka/instrumentation/callbacks/error.rb +40 -0
  58. data/lib/karafka/instrumentation/callbacks/statistics.rb +41 -0
  59. data/lib/karafka/instrumentation/logger_listener.rb +164 -0
  60. data/lib/karafka/instrumentation/monitor.rb +13 -61
  61. data/lib/karafka/instrumentation/notifications.rb +52 -0
  62. data/lib/karafka/instrumentation/proctitle_listener.rb +3 -3
  63. data/lib/karafka/instrumentation/vendors/datadog/dashboard.json +1 -0
  64. data/lib/karafka/instrumentation/vendors/datadog/listener.rb +232 -0
  65. data/lib/karafka/instrumentation.rb +21 -0
  66. data/lib/karafka/licenser.rb +75 -0
  67. data/lib/karafka/messages/batch_metadata.rb +45 -0
  68. data/lib/karafka/messages/builders/batch_metadata.rb +40 -0
  69. data/lib/karafka/messages/builders/message.rb +39 -0
  70. data/lib/karafka/messages/builders/messages.rb +32 -0
  71. data/lib/karafka/{params/params.rb → messages/message.rb} +7 -12
  72. data/lib/karafka/messages/messages.rb +64 -0
  73. data/lib/karafka/{params → messages}/metadata.rb +4 -6
  74. data/lib/karafka/messages/seek.rb +9 -0
  75. data/lib/karafka/patches/rdkafka/consumer.rb +22 -0
  76. data/lib/karafka/pro/active_job/consumer.rb +46 -0
  77. data/lib/karafka/pro/active_job/dispatcher.rb +61 -0
  78. data/lib/karafka/pro/active_job/job_options_contract.rb +32 -0
  79. data/lib/karafka/pro/base_consumer.rb +82 -0
  80. data/lib/karafka/pro/contracts/base.rb +21 -0
  81. data/lib/karafka/pro/contracts/consumer_group.rb +34 -0
  82. data/lib/karafka/pro/contracts/consumer_group_topic.rb +33 -0
  83. data/lib/karafka/pro/loader.rb +76 -0
  84. data/lib/karafka/pro/performance_tracker.rb +80 -0
  85. data/lib/karafka/pro/processing/coordinator.rb +72 -0
  86. data/lib/karafka/pro/processing/jobs/consume_non_blocking.rb +37 -0
  87. data/lib/karafka/pro/processing/jobs_builder.rb +32 -0
  88. data/lib/karafka/pro/processing/partitioner.rb +60 -0
  89. data/lib/karafka/pro/processing/scheduler.rb +56 -0
  90. data/lib/karafka/pro/routing/builder_extensions.rb +30 -0
  91. data/lib/karafka/pro/routing/topic_extensions.rb +38 -0
  92. data/lib/karafka/pro.rb +13 -0
  93. data/lib/karafka/process.rb +1 -0
  94. data/lib/karafka/processing/coordinator.rb +88 -0
  95. data/lib/karafka/processing/coordinators_buffer.rb +54 -0
  96. data/lib/karafka/processing/executor.rb +118 -0
  97. data/lib/karafka/processing/executors_buffer.rb +88 -0
  98. data/lib/karafka/processing/jobs/base.rb +51 -0
  99. data/lib/karafka/processing/jobs/consume.rb +42 -0
  100. data/lib/karafka/processing/jobs/revoked.rb +22 -0
  101. data/lib/karafka/processing/jobs/shutdown.rb +23 -0
  102. data/lib/karafka/processing/jobs_builder.rb +29 -0
  103. data/lib/karafka/processing/jobs_queue.rb +144 -0
  104. data/lib/karafka/processing/partitioner.rb +22 -0
  105. data/lib/karafka/processing/result.rb +29 -0
  106. data/lib/karafka/processing/scheduler.rb +22 -0
  107. data/lib/karafka/processing/worker.rb +88 -0
  108. data/lib/karafka/processing/workers_batch.rb +27 -0
  109. data/lib/karafka/railtie.rb +113 -0
  110. data/lib/karafka/routing/builder.rb +15 -24
  111. data/lib/karafka/routing/consumer_group.rb +11 -19
  112. data/lib/karafka/routing/consumer_mapper.rb +1 -2
  113. data/lib/karafka/routing/router.rb +1 -1
  114. data/lib/karafka/routing/subscription_group.rb +53 -0
  115. data/lib/karafka/routing/subscription_groups_builder.rb +53 -0
  116. data/lib/karafka/routing/topic.rb +61 -24
  117. data/lib/karafka/routing/topics.rb +38 -0
  118. data/lib/karafka/runner.rb +51 -0
  119. data/lib/karafka/serialization/json/deserializer.rb +6 -15
  120. data/lib/karafka/server.rb +67 -26
  121. data/lib/karafka/setup/config.rb +147 -175
  122. data/lib/karafka/status.rb +14 -5
  123. data/lib/karafka/templates/example_consumer.rb.erb +16 -0
  124. data/lib/karafka/templates/karafka.rb.erb +15 -51
  125. data/lib/karafka/time_trackers/base.rb +19 -0
  126. data/lib/karafka/time_trackers/pause.rb +92 -0
  127. data/lib/karafka/time_trackers/poll.rb +65 -0
  128. data/lib/karafka/version.rb +1 -1
  129. data/lib/karafka.rb +38 -17
  130. data.tar.gz.sig +0 -0
  131. metadata +118 -120
  132. metadata.gz.sig +0 -0
  133. data/MIT-LICENCE +0 -18
  134. data/lib/karafka/assignment_strategies/round_robin.rb +0 -13
  135. data/lib/karafka/attributes_map.rb +0 -63
  136. data/lib/karafka/backends/inline.rb +0 -16
  137. data/lib/karafka/base_responder.rb +0 -226
  138. data/lib/karafka/cli/flow.rb +0 -48
  139. data/lib/karafka/cli/missingno.rb +0 -19
  140. data/lib/karafka/code_reloader.rb +0 -67
  141. data/lib/karafka/connection/api_adapter.rb +0 -158
  142. data/lib/karafka/connection/batch_delegator.rb +0 -55
  143. data/lib/karafka/connection/builder.rb +0 -23
  144. data/lib/karafka/connection/message_delegator.rb +0 -36
  145. data/lib/karafka/consumers/batch_metadata.rb +0 -10
  146. data/lib/karafka/consumers/callbacks.rb +0 -71
  147. data/lib/karafka/consumers/includer.rb +0 -64
  148. data/lib/karafka/consumers/responders.rb +0 -24
  149. data/lib/karafka/consumers/single_params.rb +0 -15
  150. data/lib/karafka/contracts/responder_usage.rb +0 -54
  151. data/lib/karafka/fetcher.rb +0 -42
  152. data/lib/karafka/helpers/class_matcher.rb +0 -88
  153. data/lib/karafka/helpers/config_retriever.rb +0 -46
  154. data/lib/karafka/helpers/inflector.rb +0 -26
  155. data/lib/karafka/instrumentation/stdout_listener.rb +0 -140
  156. data/lib/karafka/params/batch_metadata.rb +0 -26
  157. data/lib/karafka/params/builders/batch_metadata.rb +0 -30
  158. data/lib/karafka/params/builders/params.rb +0 -38
  159. data/lib/karafka/params/builders/params_batch.rb +0 -25
  160. data/lib/karafka/params/params_batch.rb +0 -60
  161. data/lib/karafka/patches/ruby_kafka.rb +0 -47
  162. data/lib/karafka/persistence/client.rb +0 -29
  163. data/lib/karafka/persistence/consumers.rb +0 -45
  164. data/lib/karafka/persistence/topics.rb +0 -48
  165. data/lib/karafka/responders/builder.rb +0 -36
  166. data/lib/karafka/responders/topic.rb +0 -55
  167. data/lib/karafka/routing/topic_mapper.rb +0 -53
  168. data/lib/karafka/serialization/json/serializer.rb +0 -31
  169. data/lib/karafka/setup/configurators/water_drop.rb +0 -36
  170. data/lib/karafka/templates/application_responder.rb.erb +0 -11
@@ -1,19 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  <% if rails? -%>
4
- ENV['RAILS_ENV'] ||= 'development'
5
- ENV['KARAFKA_ENV'] = ENV['RAILS_ENV']
6
4
  require ::File.expand_path('../config/environment', __FILE__)
7
- Rails.application.eager_load!
8
-
9
- # This lines will make Karafka print to stdout like puma or unicorn
10
- if Rails.env.development?
11
- Rails.logger.extend(
12
- ActiveSupport::Logger.broadcast(
13
- ActiveSupport::Logger.new($stdout)
14
- )
15
- )
16
- end
17
5
  <% else -%>
18
6
  # This file is auto-generated during the install process.
19
7
  # If by any chance you've wanted a setup for Rails app, either run the `karafka:install`
@@ -31,9 +19,7 @@ APP_LOADER.enable_reloading
31
19
  %w[
32
20
  lib
33
21
  app/consumers
34
- app/responders
35
- app/workers
36
- ].each(&APP_LOADER.method(:push_dir))
22
+ ].each { |dir| APP_LOADER.push_dir(dir) }
37
23
 
38
24
  APP_LOADER.setup
39
25
  APP_LOADER.eager_load
@@ -41,10 +27,12 @@ APP_LOADER.eager_load
41
27
 
42
28
  class KarafkaApp < Karafka::App
43
29
  setup do |config|
44
- config.kafka.seed_brokers = %w[kafka://127.0.0.1:9092]
30
+ config.kafka = { 'bootstrap.servers': '127.0.0.1:9092' }
45
31
  config.client_id = 'example_app'
46
32
  <% if rails? -%>
47
- config.logger = Rails.logger
33
+ # Recreate consumers with each batch. This will allow Rails code reload to work in the
34
+ # development mode. Otherwise Karafka process would not be aware of code changes
35
+ config.consumer_persistence = !Rails.env.development?
48
36
  <% end -%>
49
37
  end
50
38
 
@@ -52,41 +40,17 @@ class KarafkaApp < Karafka::App
52
40
  # interested in logging events for certain environments. Since instrumentation
53
41
  # notifications add extra boilerplate, if you want to achieve max performance,
54
42
  # listen to only what you really need for given environment.
55
- Karafka.monitor.subscribe(WaterDrop::Instrumentation::StdoutListener.new)
56
- Karafka.monitor.subscribe(Karafka::Instrumentation::StdoutListener.new)
43
+ Karafka.monitor.subscribe(Karafka::Instrumentation::LoggerListener.new)
57
44
  # Karafka.monitor.subscribe(Karafka::Instrumentation::ProctitleListener.new)
58
45
 
59
- # Uncomment that in order to achieve code reload in development mode
60
- # Be aware, that this might have some side-effects. Please refer to the wiki
61
- # for more details on benefits and downsides of the code reload in the
62
- # development mode
63
- #
64
- # Karafka.monitor.subscribe(
65
- # Karafka::CodeReloader.new(
66
- # <%= rails? ? '*Rails.application.reloaders' : 'APP_LOADER' %>
67
- # )
68
- # )
69
-
70
- consumer_groups.draw do
71
- # topic :example do
72
- # consumer ExampleConsumer
73
- # end
74
-
75
- # consumer_group :bigger_group do
76
- # topic :test do
77
- # consumer TestConsumer
78
- # end
79
- #
80
- # topic :test2 do
81
- # consumer Test2Consumer
82
- # end
83
- # end
46
+ routes.draw do
47
+ <% if rails? -%>
48
+ # Uncomment this if you use Karafka with ActiveJob
49
+ # You ned to define the topic per each queue name you use
50
+ # active_job_topic :default
51
+ <% end -%>
52
+ topic :example do
53
+ consumer ExampleConsumer
54
+ end
84
55
  end
85
56
  end
86
-
87
- Karafka.monitor.subscribe('app.initialized') do
88
- # Put here all the things you want to do after the Karafka framework
89
- # initialization
90
- end
91
-
92
- KarafkaApp.boot!
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ # Time trackers module.
5
+ #
6
+ # Time trackers are used to track time in context of having a time poll (amount of time
7
+ # available for processing) or a pausing engine (pause for a time period).
8
+ module TimeTrackers
9
+ # Base class for all the time-trackers.
10
+ class Base
11
+ private
12
+
13
+ # @return [Float] current time in milliseconds
14
+ def now
15
+ ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) * 1000
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module TimeTrackers
5
+ # Handles Kafka topic partition pausing and resuming with exponential back-offs.
6
+ class Pause < Base
7
+ attr_reader :count
8
+
9
+ # @param timeout [Integer] how long should we wait when anything went wrong (in ms)
10
+ # @param max_timeout [Integer, nil] if exponential is on, what is the max value we can reach
11
+ # exponentially on which we will stay
12
+ # @param exponential_backoff [Boolean] should we wait exponentially or with the same
13
+ # timeout value
14
+ # @return [Karafka::TimeTrackers::Pause]
15
+ # @example
16
+ # pause = Karafka::TimeTrackers::Pause.new(timeout: 1000)
17
+ # pause.expired? #=> true
18
+ # pause.paused? #=> false
19
+ # pause.pause
20
+ # sleep(1.1)
21
+ # pause.paused? #=> true
22
+ # pause.expired? #=> true
23
+ # pause.count #=> 1
24
+ # pause.pause
25
+ # pause.count #=> 1
26
+ # pause.paused? #=> true
27
+ # pause.expired? #=> false
28
+ # pause.resume
29
+ # pause.count #=> 2
30
+ # pause.paused? #=> false
31
+ # pause.reset
32
+ # pause.count #=> 0
33
+ def initialize(timeout:, max_timeout:, exponential_backoff:)
34
+ @started_at = nil
35
+ @count = 0
36
+ @timeout = timeout
37
+ @max_timeout = max_timeout
38
+ @exponential_backoff = exponential_backoff
39
+ super()
40
+ end
41
+
42
+ # Pauses the processing from now till the end of the interval (backoff or non-backoff)
43
+ # and records the count.
44
+ # @param timeout [Integer] timeout value in milliseconds that overwrites the default timeout
45
+ # @note Providing this value can be useful when we explicitly want to pause for a certain
46
+ # period of time, outside of any regular pausing logic
47
+ def pause(timeout = backoff_interval)
48
+ @started_at = now
49
+ @ends_at = @started_at + timeout
50
+ @count += 1
51
+ end
52
+
53
+ # Marks the pause as resumed.
54
+ def resume
55
+ @started_at = nil
56
+ @ends_at = nil
57
+ end
58
+
59
+ # Expires the pause, so it can be considered expired
60
+ def expire
61
+ @ends_at = nil
62
+ end
63
+
64
+ # @return [Boolean] are we paused from processing
65
+ def paused?
66
+ !@started_at.nil?
67
+ end
68
+
69
+ # @return [Boolean] did the pause expire
70
+ def expired?
71
+ @ends_at ? now >= @ends_at : true
72
+ end
73
+
74
+ # Resets the pause counter.
75
+ def reset
76
+ @count = 0
77
+ end
78
+
79
+ private
80
+
81
+ # Computers the exponential backoff
82
+ # @return [Integer] backoff in milliseconds
83
+ def backoff_interval
84
+ backoff_factor = @exponential_backoff ? 2**@count : 1
85
+
86
+ timeout = backoff_factor * @timeout
87
+
88
+ @max_timeout && timeout > @max_timeout ? @max_timeout : timeout
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module TimeTrackers
5
+ # Object used to keep track of time we've used running certain operations.
6
+ #
7
+ # @example Keep track of sleeping and stop after 3 seconds of 0.1 sleep intervals
8
+ # time_poll = Poll.new(3000)
9
+ # time_poll.start
10
+ #
11
+ # until time_poll.exceeded?
12
+ # time_poll.start
13
+ # puts "I have #{time_poll.remaining.to_i}ms remaining to sleep..."
14
+ # sleep(0.1)
15
+ # time_poll.checkpoint
16
+ # end
17
+ class Poll < Base
18
+ attr_reader :remaining, :attempts
19
+
20
+ # @param total_time [Integer] amount of milliseconds before we exceed the given time limit
21
+ # @return [TimeTracker] time poll instance
22
+ def initialize(total_time)
23
+ @remaining = total_time
24
+ @attempts = 0
25
+ super()
26
+ end
27
+
28
+ # @return [Boolean] did we exceed the time limit
29
+ def exceeded?
30
+ @remaining <= 0
31
+ end
32
+
33
+ # Starts time tracking.
34
+ def start
35
+ @attempts += 1
36
+ @started_at = now
37
+ end
38
+
39
+ # Stops time tracking of a given piece of code and updates the remaining time.
40
+ def checkpoint
41
+ @remaining -= (now - @started_at)
42
+ end
43
+
44
+ # @return [Boolean] If anything went wrong, can we retry after a backoff period or not
45
+ # (do we have enough time)
46
+ def retryable?
47
+ remaining > backoff_interval
48
+ end
49
+
50
+ # Sleeps for amount of time matching attempt, so we sleep more with each attempt in case of
51
+ # a retry.
52
+ def backoff
53
+ # Sleep requires seconds not ms
54
+ sleep(backoff_interval / 1_000.0)
55
+ end
56
+
57
+ private
58
+
59
+ # @return [Integer] milliseconds of the backoff time
60
+ def backoff_interval
61
+ 100 * attempts
62
+ end
63
+ end
64
+ end
65
+ end
@@ -3,5 +3,5 @@
3
3
  # Main module namespace
4
4
  module Karafka
5
5
  # Current Karafka version
6
- VERSION = '1.4.13'
6
+ VERSION = '2.0.0'
7
7
  end
data/lib/karafka.rb CHANGED
@@ -1,34 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  %w[
4
+ karafka-core
4
5
  delegate
5
6
  English
7
+ rdkafka
6
8
  waterdrop
7
- kafka
8
- envlogic
9
9
  json
10
10
  thor
11
11
  forwardable
12
12
  fileutils
13
- dry-configurable
14
- dry-validation
15
- dry/events/publisher
16
- dry/inflector
17
- dry/monitor/notifications
18
- dry/core/constants
13
+ openssl
14
+ base64
15
+ date
19
16
  zeitwerk
20
17
  ].each(&method(:require))
21
18
 
22
- # Karafka library
19
+ # Karafka framework main namespace
23
20
  module Karafka
24
- extend Envlogic
25
-
26
21
  class << self
22
+ # @return [Karafka::Env] env instance that allows us to check environment
23
+ def env
24
+ @env ||= Env.new
25
+ end
26
+
27
+ # @param environment [String, Symbol] new environment that we want to set
28
+ # @return [Karafka::Env] env instance
29
+ # @example Assign new environment to Karafka::App
30
+ # Karafka::App.env = :production
31
+ def env=(environment)
32
+ env.replace(environment.to_s)
33
+ end
34
+
27
35
  # @return [Logger] logger that we want to use. Will use ::Karafka::Logger by default
28
36
  def logger
29
37
  @logger ||= App.config.logger
30
38
  end
31
39
 
40
+ # @return [WaterDrop::Producer] waterdrop messages producer
41
+ def producer
42
+ @producer ||= App.config.producer
43
+ end
44
+
32
45
  # @return [::Karafka::Monitor] monitor that we want to use
33
46
  def monitor
34
47
  @monitor ||= App.config.monitor
@@ -49,6 +62,11 @@ module Karafka
49
62
  Pathname.new(File.expand_path('karafka', __dir__))
50
63
  end
51
64
 
65
+ # @return [Boolean] true if there is a valid pro token present
66
+ def pro?
67
+ App.config.license.token != false
68
+ end
69
+
52
70
  # @return [String] path to a default file that contains booting procedure etc
53
71
  # @note By default it is a file called 'karafka.rb' but it can be specified as you wish if you
54
72
  # have Karafka that is merged into a Sinatra/Rails app and karafka.rb is taken.
@@ -64,9 +82,12 @@ module Karafka
64
82
  end
65
83
  end
66
84
 
67
- Zeitwerk::Loader
68
- .for_gem
69
- .tap(&:setup)
70
- .tap(&:eager_load)
71
-
72
- Kafka::Consumer.prepend(Karafka::Patches::RubyKafka)
85
+ loader = Zeitwerk::Loader.for_gem
86
+ # Do not load Rails extensions by default, this will be handled by Railtie if they are needed
87
+ loader.ignore(Karafka.gem_root.join('lib/active_job'))
88
+ # Do not load pro components, this will be handled by license manager
89
+ loader.ignore(Karafka.gem_root.join('lib/karafka/pro'))
90
+ # Do not load vendors instrumentation components. Those need to be required manually if needed
91
+ loader.ignore(Karafka.gem_root.join('lib/karafka/instrumentation/vendors'))
92
+ loader.setup
93
+ loader.eager_load
data.tar.gz.sig CHANGED
Binary file