karafka 2.0.0.alpha1 → 2.0.0.alpha4

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 (56) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/ci.yml +3 -1
  4. data/CHANGELOG.md +14 -1
  5. data/CONTRIBUTING.md +6 -6
  6. data/Gemfile.lock +24 -24
  7. data/LICENSE +3 -0
  8. data/bin/integrations +44 -8
  9. data/bin/karafka +4 -0
  10. data/bin/stress +1 -1
  11. data/config/errors.yml +1 -0
  12. data/docker-compose.yml +1 -0
  13. data/karafka.gemspec +1 -1
  14. data/lib/active_job/karafka.rb +16 -13
  15. data/lib/active_job/queue_adapters/karafka_adapter.rb +3 -6
  16. data/lib/karafka/active_job/consumer.rb +24 -0
  17. data/lib/karafka/active_job/dispatcher.rb +38 -0
  18. data/lib/karafka/active_job/job_extensions.rb +34 -0
  19. data/lib/karafka/active_job/job_options_contract.rb +15 -0
  20. data/lib/karafka/active_job/routing_extensions.rb +18 -0
  21. data/lib/karafka/app.rb +1 -0
  22. data/lib/karafka/cli/info.rb +3 -3
  23. data/lib/karafka/cli/install.rb +1 -0
  24. data/lib/karafka/cli/server.rb +2 -16
  25. data/lib/karafka/contracts/base.rb +23 -0
  26. data/lib/karafka/contracts/config.rb +21 -3
  27. data/lib/karafka/contracts/consumer_group.rb +1 -3
  28. data/lib/karafka/contracts/consumer_group_topic.rb +2 -3
  29. data/lib/karafka/contracts/server_cli_options.rb +1 -3
  30. data/lib/karafka/errors.rb +4 -0
  31. data/lib/karafka/instrumentation/monitor.rb +1 -0
  32. data/lib/karafka/instrumentation/stdout_listener.rb +3 -0
  33. data/lib/karafka/licenser.rb +20 -9
  34. data/lib/karafka/messages/batch_metadata.rb +2 -0
  35. data/lib/karafka/messages/builders/batch_metadata.rb +23 -1
  36. data/lib/karafka/pro/active_job/dispatcher.rb +58 -0
  37. data/lib/karafka/pro/active_job/job_options_contract.rb +27 -0
  38. data/lib/karafka/pro/loader.rb +29 -0
  39. data/lib/karafka/pro.rb +13 -0
  40. data/lib/karafka/processing/worker.rb +1 -1
  41. data/lib/karafka/railtie.rb +55 -19
  42. data/lib/karafka/routing/builder.rb +1 -11
  43. data/lib/karafka/routing/subscription_group.rb +5 -5
  44. data/lib/karafka/routing/subscription_groups_builder.rb +1 -0
  45. data/lib/karafka/routing/topic.rb +1 -0
  46. data/lib/karafka/setup/config.rb +25 -20
  47. data/lib/karafka/status.rb +1 -0
  48. data/lib/karafka/templates/karafka.rb.erb +1 -1
  49. data/lib/karafka/version.rb +1 -1
  50. data/lib/karafka.rb +7 -2
  51. data.tar.gz.sig +0 -0
  52. metadata +14 -7
  53. metadata.gz.sig +0 -0
  54. data/.github/FUNDING.yml +0 -3
  55. data/lib/active_job/consumer.rb +0 -22
  56. data/lib/active_job/routing_extensions.rb +0 -15
@@ -8,9 +8,7 @@ module Karafka
8
8
  # `Karafka::Setup::Config` model, but we don't validate them here as they are
9
9
  # validated per each route (topic + consumer_group) because they can be overwritten,
10
10
  # so we validate all of that once all the routes are defined and ready.
11
- class Config < Dry::Validation::Contract
12
- config.messages.load_paths << File.join(Karafka.gem_root, 'config', 'errors.yml')
13
-
11
+ class Config < Base
14
12
  params do
15
13
  # License validity happens in the licenser. Here we do only the simple consistency checks
16
14
  required(:license).schema do
@@ -27,6 +25,26 @@ module Karafka
27
25
  required(:pause_max_timeout) { int? & gt?(0) }
28
26
  required(:pause_with_exponential_backoff).filled(:bool?)
29
27
  required(:shutdown_timeout) { int? & gt?(0) }
28
+ required(:kafka).filled(:hash)
29
+
30
+ # We validate internals just to be sure, that they are present and working
31
+ required(:internal).schema do
32
+ required(:routing_builder)
33
+ required(:status)
34
+ required(:process)
35
+ required(:subscription_groups_builder)
36
+ end
37
+ end
38
+
39
+ # rdkafka requires all the keys to be strings, so we ensure that
40
+ rule(:kafka) do
41
+ next unless value.is_a?(Hash)
42
+
43
+ value.each_key do |key|
44
+ next if key.is_a?(Symbol)
45
+
46
+ key(:"kafka.#{key}").failure(:kafka_key_must_be_a_symbol)
47
+ end
30
48
  end
31
49
 
32
50
  rule(:pause_timeout, :pause_max_timeout) do
@@ -3,9 +3,7 @@
3
3
  module Karafka
4
4
  module Contracts
5
5
  # Contract for single full route (consumer group + topics) validation.
6
- class ConsumerGroup < Dry::Validation::Contract
7
- config.messages.load_paths << File.join(Karafka.gem_root, 'config', 'errors.yml')
8
-
6
+ class ConsumerGroup < Base
9
7
  # Internal contract for sub-validating topics schema
10
8
  TOPIC_CONTRACT = ConsumerGroupTopic.new.freeze
11
9
 
@@ -3,15 +3,14 @@
3
3
  module Karafka
4
4
  module Contracts
5
5
  # Consumer group topic validation rules.
6
- class ConsumerGroupTopic < Dry::Validation::Contract
7
- config.messages.load_paths << File.join(Karafka.gem_root, 'config', 'errors.yml')
8
-
6
+ class ConsumerGroupTopic < Base
9
7
  params do
10
8
  required(:consumer).filled
11
9
  required(:deserializer).filled
12
10
  required(:id).filled(:str?, format?: Karafka::Contracts::TOPIC_REGEXP)
13
11
  required(:kafka).filled
14
12
  required(:max_messages) { int? & gteq?(1) }
13
+ required(:initial_offset).filled(included_in?: %w[earliest latest])
15
14
  required(:max_wait_time).filled { int? & gteq?(10) }
16
15
  required(:manual_offset_management).filled(:bool?)
17
16
  required(:name).filled(:str?, format?: Karafka::Contracts::TOPIC_REGEXP)
@@ -3,9 +3,7 @@
3
3
  module Karafka
4
4
  module Contracts
5
5
  # Contract for validating correctness of the server cli command options.
6
- class ServerCliOptions < Dry::Validation::Contract
7
- config.messages.load_paths << File.join(Karafka.gem_root, 'config', 'errors.yml')
8
-
6
+ class ServerCliOptions < Base
9
7
  params do
10
8
  optional(:consumer_groups).value(:array, :filled?)
11
9
  end
@@ -43,5 +43,9 @@ module Karafka
43
43
 
44
44
  # Raised when the license token is not valid
45
45
  InvalidLicenseTokenError = Class.new(BaseError)
46
+
47
+ # Used to instrument this error into the error notifications
48
+ # We do not raise it so we won't crash deployed systems
49
+ ExpiredLicenseTokenError = Class.new(BaseError)
46
50
  end
47
51
  end
@@ -17,6 +17,7 @@ module Karafka
17
17
  # complete list of all the events. Please use the #available_events on fully loaded
18
18
  # Karafka system to determine all of the events you can use.
19
19
  BASE_EVENTS = %w[
20
+ app.initialized
20
21
  app.running
21
22
  app.stopping
22
23
  app.stopped
@@ -88,6 +88,9 @@ module Karafka
88
88
  when 'connection.listener.fetch_loop.error'
89
89
  error "Listener fetch loop error: #{error}"
90
90
  error details
91
+ when 'licenser.expired'
92
+ error error
93
+ error details
91
94
  when 'runner.call.error'
92
95
  fatal "Runner crashed due to an error: #{error}"
93
96
  fatal details
@@ -26,7 +26,7 @@ module Karafka
26
26
  data = nil
27
27
  end
28
28
 
29
- details = data ? JSON.parse(data) : raise_invalid_license_token
29
+ details = data ? JSON.parse(data) : raise_invalid_license_token(license_config)
30
30
 
31
31
  license_config.entity = details.fetch('entity')
32
32
  license_config.expires_on = Date.parse(details.fetch('expires_on'))
@@ -39,7 +39,11 @@ module Karafka
39
39
  private
40
40
 
41
41
  # Raises an error with info, that used token is invalid
42
- def raise_invalid_license_token
42
+ # @param license_config [Dry::Configurable::Config]
43
+ def raise_invalid_license_token(license_config)
44
+ # We set it to false so `Karafka.pro?` method behaves as expected
45
+ license_config.token = false
46
+
43
47
  raise(
44
48
  Errors::InvalidLicenseTokenError,
45
49
  <<~MSG.tr("\n", ' ')
@@ -50,15 +54,22 @@ module Karafka
50
54
  end
51
55
 
52
56
  # We do not raise an error here as we don't want to cause any problems to someone that runs
53
- # Karafka on production. Error is enough.
57
+ # Karafka on production. Error message is enough.
58
+ #
54
59
  # @param expires_on [Date] when the license expires
55
60
  def notify_if_license_expired(expires_on)
56
- Karafka.logger.error(
57
- <<~MSG.tr("\n", ' ')
58
- Your license expired on #{expires_on}.
59
- Karafka no longer uses any Pro capabilities.
60
- Please reach us at contact@karafka.io or visit https://karafka.io to obtain a valid one.
61
- MSG
61
+ message = <<~MSG.tr("\n", ' ')
62
+ Your license expired on #{expires_on}.
63
+ Please reach us at contact@karafka.io or visit https://karafka.io to obtain a valid one.
64
+ MSG
65
+
66
+ Karafka.logger.error(message)
67
+
68
+ Karafka.monitor.instrument(
69
+ 'error.occurred',
70
+ caller: self,
71
+ error: Errors::ExpiredLicenseTokenError.new(message),
72
+ type: 'licenser.expired'
62
73
  )
63
74
  end
64
75
  end
@@ -14,6 +14,8 @@ module Karafka
14
14
  :partition,
15
15
  :topic,
16
16
  :scheduled_at,
17
+ :consumption_lag,
18
+ :processing_lag,
17
19
  keyword_init: true
18
20
  )
19
21
  end
@@ -12,7 +12,12 @@ module Karafka
12
12
  # @param topic [Karafka::Routing::Topic] topic for which we've fetched the batch
13
13
  # @param scheduled_at [Time] moment when the batch was scheduled for processing
14
14
  # @return [Karafka::Messages::BatchMetadata] batch metadata object
15
+ # @note Regarding the time lags: we can use the current time here, as batch metadata is
16
+ # created in the worker. So whenever this is being built, it means that the processing
17
+ # of this batch has already started.
15
18
  def call(kafka_batch, topic, scheduled_at)
19
+ now = Time.now
20
+
16
21
  Karafka::Messages::BatchMetadata.new(
17
22
  size: kafka_batch.count,
18
23
  first_offset: kafka_batch.first.offset,
@@ -20,9 +25,26 @@ module Karafka
20
25
  deserializer: topic.deserializer,
21
26
  partition: kafka_batch[0].partition,
22
27
  topic: topic.name,
23
- scheduled_at: scheduled_at
28
+ scheduled_at: scheduled_at,
29
+ # This lag describes how long did it take for a message to be consumed from the
30
+ # moment it was created
31
+ consumption_lag: time_distance_in_ms(now, kafka_batch.last.timestamp),
32
+ # This lag describes how long did a batch have to wait before it was picked up by
33
+ # one of the workers
34
+ processing_lag: time_distance_in_ms(now, scheduled_at)
24
35
  ).freeze
25
36
  end
37
+
38
+ private
39
+
40
+ # Computes time distance in between two times in ms
41
+ #
42
+ # @param time1 [Time]
43
+ # @param time2 [Time]
44
+ # @return [Integer] distance in between two times in ms
45
+ def time_distance_in_ms(time1, time2)
46
+ ((time1 - time2) * 1_000).round
47
+ end
26
48
  end
27
49
  end
28
50
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This Karafka component is a Pro component.
4
+ # All of the commercial components are present in the lib/karafka/pro directory of this repository
5
+ # and their usage requires commercial license agreement.
6
+ #
7
+ # Karafka has also commercial-friendly license, commercial support and commercial components.
8
+ #
9
+ # By sending a pull request to the pro components, you are agreeing to transfer the copyright of
10
+ # your code to Maciej Mensfeld.
11
+
12
+ module Karafka
13
+ module Pro
14
+ # Karafka Pro ActiveJob components
15
+ module ActiveJob
16
+ # Pro dispatcher that sends the ActiveJob job to a proper topic based on the queue name
17
+ # and that allows to inject additional options into the producer, effectively allowing for a
18
+ # much better and more granular control over the dispatch and consumption process.
19
+ class Dispatcher < ::Karafka::ActiveJob::Dispatcher
20
+ # Defaults for dispatching
21
+ # The can be updated by using `#karafka_options` on the job
22
+ DEFAULTS = {
23
+ dispatch_method: :produce_async,
24
+ # We don't create a dummy proc based partitioner as we would have to evaluate it with
25
+ # each job.
26
+ partitioner: nil
27
+ }.freeze
28
+
29
+ private_constant :DEFAULTS
30
+
31
+ # @param job [ActiveJob::Base] job
32
+ def call(job)
33
+ ::Karafka.producer.public_send(
34
+ fetch_option(job, :dispatch_method, DEFAULTS),
35
+ dispatch_details(job).merge!(
36
+ topic: job.queue_name,
37
+ payload: ::ActiveSupport::JSON.encode(job.serialize)
38
+ )
39
+ )
40
+ end
41
+
42
+ private
43
+
44
+ # @param job [ActiveJob::Base] job instance
45
+ # @return [Hash] hash with dispatch details to which we merge topic and payload
46
+ def dispatch_details(job)
47
+ partitioner = fetch_option(job, :partitioner, DEFAULTS)
48
+
49
+ return {} unless partitioner
50
+
51
+ {
52
+ partition_key: partitioner.call(job)
53
+ }
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This Karafka component is a Pro component.
4
+ # All of the commercial components are present in the lib/karafka/pro directory of this repository
5
+ # and their usage requires commercial license agreement.
6
+ #
7
+ # Karafka has also commercial-friendly license, commercial support and commercial components.
8
+ #
9
+ # By sending a pull request to the pro components, you are agreeing to transfer the copyright of
10
+ # your code to Maciej Mensfeld.
11
+
12
+ module Karafka
13
+ module Pro
14
+ module ActiveJob
15
+ # Contract for validating the options that can be altered with `#karafka_options` per job
16
+ # class that works with Pro features.
17
+ class JobOptionsContract < ::Karafka::ActiveJob::JobOptionsContract
18
+ # Dry types
19
+ Types = include Dry.Types()
20
+
21
+ params do
22
+ optional(:partitioner).value(Types.Interface(:call))
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This Karafka component is a Pro component.
4
+ # All of the commercial components are present in the lib/karafka/pro directory of this repository
5
+ # and their usage requires commercial license agreement.
6
+ #
7
+ # Karafka has also commercial-friendly license, commercial support and commercial components.
8
+ #
9
+ # By sending a pull request to the pro components, you are agreeing to transfer the copyright of
10
+ # your code to Maciej Mensfeld.
11
+ module Karafka
12
+ module Pro
13
+ # Loader requires and loads all the pro components only when they are needed
14
+ class Loader
15
+ class << self
16
+ # Loads all the pro components and configures them wherever it is expected
17
+ # @param config [Dry::Configurable::Config] whole app config that we can alter with pro
18
+ # components
19
+ def setup(config)
20
+ require_relative 'active_job/dispatcher'
21
+ require_relative 'active_job/job_options_contract'
22
+
23
+ config.internal.active_job.dispatcher = ActiveJob::Dispatcher.new
24
+ config.internal.active_job.job_options_contract = ActiveJob::JobOptionsContract.new
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This Karafka component is a Pro component.
4
+ # All of the commercial components are present in the lib/karafka/pro directory of this repository
5
+ # and their usage requires commercial license agreement.
6
+ #
7
+ # Karafka has also commercial-friendly license, commercial support and commercial components.
8
+ #
9
+ module Karafka
10
+ # Namespace for pro components, licensed under the commercial license agreement.
11
+ module Pro
12
+ end
13
+ end
@@ -41,7 +41,7 @@ module Karafka
41
41
  # We signal critical exceptions, notify and do not allow worker to fail
42
42
  # rubocop:disable Lint/RescueException
43
43
  rescue Exception => e
44
- # rubocop:enable Lint/RescueException
44
+ # rubocop:enable Lint/RescueException
45
45
  Karafka.monitor.instrument(
46
46
  'error.occurred',
47
47
  caller: self,
@@ -19,6 +19,7 @@ end
19
19
  if rails
20
20
  # Load Karafka
21
21
  require 'karafka'
22
+
22
23
  # Load ActiveJob adapter
23
24
  require 'active_job/karafka'
24
25
 
@@ -30,34 +31,69 @@ if rails
30
31
  class Railtie < Rails::Railtie
31
32
  railtie_name :karafka
32
33
 
33
- initializer 'karafka.configure_rails_initialization' do |app|
34
- # Consumers should autoload by default in the Rails app so they are visible
35
- app.config.autoload_paths += %w[app/consumers]
34
+ initializer 'karafka.active_job_integration' do
35
+ ActiveSupport.on_load(:active_job) do
36
+ # Extend ActiveJob with some Karafka specific ActiveJob magic
37
+ extend ::Karafka::ActiveJob::JobExtensions
38
+ end
39
+ end
36
40
 
41
+ # This lines will make Karafka print to stdout like puma or unicorn when we run karafka
42
+ # server + will support code reloading with each fetched loop. We do it only for karafka
43
+ # based commands as Rails processes and console will have it enabled already
44
+ initializer 'karafka.configure_rails_logger' do
37
45
  # Make Karafka use Rails logger
38
46
  ::Karafka::App.config.logger = Rails.logger
39
47
 
40
- # This lines will make Karafka print to stdout like puma or unicorn
41
- if Rails.env.development?
42
- Rails.logger.extend(
43
- ActiveSupport::Logger.broadcast(
44
- ActiveSupport::Logger.new($stdout)
45
- )
48
+ next unless Rails.env.development?
49
+ next unless ENV.key?('KARAFKA_CLI')
50
+
51
+ Rails.logger.extend(
52
+ ActiveSupport::Logger.broadcast(
53
+ ActiveSupport::Logger.new($stdout)
46
54
  )
55
+ )
56
+ end
47
57
 
48
- # We can have many listeners, but it does not matter in which we will reload the code as
49
- # long as all the consumers will be re-created as Rails reload is thread-safe
50
- ::Karafka::App.monitor.subscribe('connection.listener.fetch_loop') do
51
- # Reload code each time there is a change in the code
52
- next unless Rails.application.reloaders.any?(&:updated?)
58
+ initializer 'karafka.configure_rails_auto_load_paths' do |app|
59
+ # Consumers should autoload by default in the Rails app so they are visible
60
+ app.config.autoload_paths += %w[app/consumers]
61
+ end
53
62
 
54
- Rails.application.reloader.reload!
55
- end
63
+ initializer 'karafka.configure_rails_code_reloader' do
64
+ # There are components that won't work with older Rails version, so we check it and
65
+ # provide a failover
66
+ rails6plus = Rails.gem_version >= Gem::Version.new('6.0.0')
67
+
68
+ next unless Rails.env.development?
69
+ next unless ENV.key?('KARAFKA_CLI')
70
+ next unless rails6plus
71
+
72
+ # We can have many listeners, but it does not matter in which we will reload the code
73
+ # as long as all the consumers will be re-created as Rails reload is thread-safe
74
+ ::Karafka::App.monitor.subscribe('connection.listener.fetch_loop') do
75
+ # Reload code each time there is a change in the code
76
+ next unless Rails.application.reloaders.any?(&:updated?)
77
+
78
+ Rails.application.reloader.reload!
56
79
  end
80
+ end
57
81
 
58
- app.reloader.to_prepare do
59
- # Load Karafka bot file, so it can be used in Rails server context
60
- require Rails.root.join(Karafka.boot_file.to_s).to_s
82
+ initializer 'karafka.require_karafka_boot_file' do |app|
83
+ rails6plus = Rails.gem_version >= Gem::Version.new('6.0.0')
84
+
85
+ karafka_boot_file = Rails.root.join(Karafka.boot_file.to_s).to_s
86
+
87
+ if rails6plus
88
+ app.reloader.to_prepare do
89
+ # Load Karafka boot file, so it can be used in Rails server context
90
+ require karafka_boot_file
91
+ end
92
+ else
93
+ # Load Karafka main setup for older Rails versions
94
+ app.config.after_initialize do
95
+ require karafka_boot_file
96
+ end
61
97
  end
62
98
  end
63
99
  end
@@ -10,11 +10,6 @@ module Karafka
10
10
  # end
11
11
  # end
12
12
  class Builder < Concurrent::Array
13
- # Consumer group consistency checking contract
14
- CONTRACT = Karafka::Contracts::ConsumerGroup.new.freeze
15
-
16
- private_constant :CONTRACT
17
-
18
13
  def initialize
19
14
  @draws = Concurrent::Array.new
20
15
  super
@@ -38,12 +33,7 @@ module Karafka
38
33
  instance_eval(&block)
39
34
 
40
35
  each do |consumer_group|
41
- hashed_group = consumer_group.to_h
42
- validation_result = CONTRACT.call(hashed_group)
43
-
44
- next if validation_result.success?
45
-
46
- raise Errors::InvalidConfigurationError, validation_result.errors.to_h
36
+ Contracts::ConsumerGroup.new.validate!(consumer_group.to_h)
47
37
  end
48
38
  end
49
39
 
@@ -20,7 +20,7 @@ module Karafka
20
20
 
21
21
  # @return [String] consumer group id
22
22
  def consumer_group_id
23
- kafka['group.id']
23
+ kafka[:'group.id']
24
24
  end
25
25
 
26
26
  # @return [Integer] max messages fetched in a single go
@@ -39,12 +39,12 @@ module Karafka
39
39
  def kafka
40
40
  kafka = @topics.first.kafka.dup
41
41
 
42
- kafka['client.id'] ||= Karafka::App.config.client_id
43
- kafka['group.id'] ||= @topics.first.consumer_group.id
44
- kafka['auto.offset.reset'] ||= 'earliest'
42
+ kafka[:'client.id'] ||= Karafka::App.config.client_id
43
+ kafka[:'group.id'] ||= @topics.first.consumer_group.id
44
+ kafka[:'auto.offset.reset'] ||= @topics.first.initial_offset
45
45
  # Karafka manages the offsets based on the processing state, thus we do not rely on the
46
46
  # rdkafka offset auto-storing
47
- kafka['enable.auto.offset.store'] = 'false'
47
+ kafka[:'enable.auto.offset.store'] = 'false'
48
48
  kafka.freeze
49
49
  kafka
50
50
  end
@@ -18,6 +18,7 @@ module Karafka
18
18
  kafka
19
19
  max_messages
20
20
  max_wait_time
21
+ initial_offset
21
22
  ].freeze
22
23
 
23
24
  private_constant :DISTRIBUTION_KEYS
@@ -16,6 +16,7 @@ module Karafka
16
16
  manual_offset_management
17
17
  max_messages
18
18
  max_wait_time
19
+ initial_offset
19
20
  ].freeze
20
21
 
21
22
  private_constant :INHERITABLE_ATTRIBUTES
@@ -14,15 +14,12 @@ module Karafka
14
14
  class Config
15
15
  extend Dry::Configurable
16
16
 
17
- # Contract for checking the config provided by the user
18
- CONTRACT = Karafka::Contracts::Config.new.freeze
19
-
20
17
  # Defaults for kafka settings, that will be overwritten only if not present already
21
18
  KAFKA_DEFAULTS = {
22
- 'client.id' => 'karafka'
19
+ 'client.id': 'karafka'
23
20
  }.freeze
24
21
 
25
- private_constant :CONTRACT, :KAFKA_DEFAULTS
22
+ private_constant :KAFKA_DEFAULTS
26
23
 
27
24
  # Available settings
28
25
 
@@ -57,6 +54,9 @@ module Karafka
57
54
  setting :consumer_persistence, default: true
58
55
  # Default deserializer for converting incoming data into ruby objects
59
56
  setting :deserializer, default: Karafka::Serialization::Json::Deserializer.new
57
+ # option [String] should we start with the earliest possible offset or latest
58
+ # This will set the `auto.offset.reset` value unless present in the kafka scope
59
+ setting :initial_offset, default: 'earliest'
60
60
  # option [Boolean] should we leave offset management to the user
61
61
  setting :manual_offset_management, default: false
62
62
  # options max_messages [Integer] how many messages do we want to fetch from Kafka in one go
@@ -84,8 +84,6 @@ module Karafka
84
84
  setting :kafka, default: {}
85
85
 
86
86
  # Namespace for internal settings that should not be modified
87
- # It's a temporary step to "declassify" several things internally before we move to a
88
- # non global state
89
87
  setting :internal do
90
88
  # option routing_builder [Karafka::Routing::Builder] builder instance
91
89
  setting :routing_builder, default: Routing::Builder.new
@@ -98,6 +96,17 @@ module Karafka
98
96
  # option subscription_groups_builder [Routing::SubscriptionGroupsBuilder] subscription
99
97
  # group builder
100
98
  setting :subscription_groups_builder, default: Routing::SubscriptionGroupsBuilder.new
99
+
100
+ # Karafka components for ActiveJob
101
+ setting :active_job do
102
+ # option dispatcher [Karafka::ActiveJob::Dispatcher] default dispatcher for ActiveJob
103
+ setting :dispatcher, default: ActiveJob::Dispatcher.new
104
+ # option job_options_contract [Karafka::Contracts::JobOptionsContract] contract for
105
+ # ensuring, that extra job options defined are valid
106
+ setting :job_options_contract, default: ActiveJob::JobOptionsContract.new
107
+ # option consumer [Class] consumer class that should be used to consume ActiveJob data
108
+ setting :consumer, default: ActiveJob::Consumer
109
+ end
101
110
  end
102
111
 
103
112
  class << self
@@ -106,12 +115,14 @@ module Karafka
106
115
  def setup(&block)
107
116
  configure(&block)
108
117
  merge_kafka_defaults!(config)
109
- validate!
118
+ Contracts::Config.new.validate!(config.to_h)
110
119
 
111
120
  # Check the license presence (if needed) and
112
121
  Licenser.new.verify(config.license)
113
122
 
114
123
  configure_components
124
+
125
+ Karafka::App.initialized!
115
126
  end
116
127
 
117
128
  private
@@ -128,18 +139,6 @@ module Karafka
128
139
  end
129
140
  end
130
141
 
131
- # Validate config based on the config contract
132
- # @return [Boolean] true if configuration is valid
133
- # @raise [Karafka::Errors::InvalidConfigurationError] raised when configuration
134
- # doesn't match with the config contract
135
- def validate!
136
- validation_result = CONTRACT.call(config.to_h)
137
-
138
- return true if validation_result.success?
139
-
140
- raise Errors::InvalidConfigurationError, validation_result.errors.to_h
141
- end
142
-
143
142
  # Sets up all the components that are based on the user configuration
144
143
  # @note At the moment it is only WaterDrop
145
144
  def configure_components
@@ -149,6 +148,12 @@ module Karafka
149
148
  producer_config.kafka = config.kafka.dup
150
149
  producer_config.logger = config.logger
151
150
  end
151
+
152
+ return unless Karafka.pro?
153
+
154
+ # Runs the pro loader that includes all the pro components
155
+ require 'karafka/pro/loader'
156
+ Pro::Loader.setup(config)
152
157
  end
153
158
  end
154
159
  end
@@ -6,6 +6,7 @@ module Karafka
6
6
  # Available states and their transitions.
7
7
  STATES = {
8
8
  initializing: :initialize!,
9
+ initialized: :initialized!,
9
10
  running: :run!,
10
11
  stopping: :stop!,
11
12
  stopped: :stopped!
@@ -27,7 +27,7 @@ APP_LOADER.eager_load
27
27
 
28
28
  class KarafkaApp < Karafka::App
29
29
  setup do |config|
30
- config.kafka = { 'bootstrap.servers' => '127.0.0.1:9092' }
30
+ config.kafka = { 'bootstrap.servers': '127.0.0.1:9092' }
31
31
  config.client_id = 'example_app'
32
32
  <% if rails? -%>
33
33
  # Recreate consumers with each batch. This will allow Rails code reload to work in the
@@ -3,5 +3,5 @@
3
3
  # Main module namespace
4
4
  module Karafka
5
5
  # Current Karafka version
6
- VERSION = '2.0.0.alpha1'
6
+ VERSION = '2.0.0.alpha4'
7
7
  end