karafka 2.0.0.beta2 → 2.0.0.beta5

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 (58) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/ci.yml +18 -15
  4. data/CHANGELOG.md +49 -0
  5. data/Gemfile.lock +8 -8
  6. data/bin/benchmarks +2 -2
  7. data/bin/integrations +44 -15
  8. data/bin/scenario +29 -0
  9. data/bin/{stress → stress_many} +0 -0
  10. data/bin/stress_one +13 -0
  11. data/bin/wait_for_kafka +20 -0
  12. data/docker-compose.yml +28 -11
  13. data/karafka.gemspec +2 -2
  14. data/lib/karafka/active_job/routing/extensions.rb +12 -2
  15. data/lib/karafka/app.rb +2 -1
  16. data/lib/karafka/base_consumer.rb +75 -45
  17. data/lib/karafka/connection/client.rb +88 -22
  18. data/lib/karafka/connection/listener.rb +60 -18
  19. data/lib/karafka/connection/pauses_manager.rb +8 -0
  20. data/lib/karafka/connection/rebalance_manager.rb +20 -19
  21. data/lib/karafka/contracts/config.rb +17 -3
  22. data/lib/karafka/contracts/server_cli_options.rb +1 -1
  23. data/lib/karafka/errors.rb +3 -0
  24. data/lib/karafka/instrumentation/logger_listener.rb +34 -10
  25. data/lib/karafka/instrumentation/monitor.rb +3 -1
  26. data/lib/karafka/licenser.rb +26 -7
  27. data/lib/karafka/pro/active_job/consumer.rb +30 -9
  28. data/lib/karafka/pro/active_job/dispatcher.rb +9 -9
  29. data/lib/karafka/pro/active_job/job_options_contract.rb +9 -9
  30. data/lib/karafka/pro/base_consumer.rb +73 -0
  31. data/lib/karafka/pro/loader.rb +38 -20
  32. data/lib/karafka/pro/performance_tracker.rb +9 -9
  33. data/lib/karafka/pro/processing/coordinator.rb +12 -0
  34. data/lib/karafka/pro/processing/jobs/consume_non_blocking.rb +10 -11
  35. data/lib/karafka/pro/processing/jobs_builder.rb +32 -0
  36. data/lib/karafka/pro/processing/scheduler.rb +56 -0
  37. data/lib/karafka/pro/routing/extensions.rb +32 -0
  38. data/lib/karafka/processing/coordinator.rb +84 -0
  39. data/lib/karafka/processing/coordinators_buffer.rb +58 -0
  40. data/lib/karafka/processing/executor.rb +23 -9
  41. data/lib/karafka/processing/executors_buffer.rb +46 -15
  42. data/lib/karafka/processing/jobs/base.rb +8 -3
  43. data/lib/karafka/processing/jobs/consume.rb +11 -4
  44. data/lib/karafka/processing/jobs_builder.rb +29 -0
  45. data/lib/karafka/processing/result.rb +29 -0
  46. data/lib/karafka/processing/scheduler.rb +22 -0
  47. data/lib/karafka/processing/worker.rb +17 -9
  48. data/lib/karafka/routing/consumer_group.rb +1 -1
  49. data/lib/karafka/routing/subscription_group.rb +1 -1
  50. data/lib/karafka/routing/topic.rb +14 -0
  51. data/lib/karafka/setup/config.rb +19 -9
  52. data/lib/karafka/status.rb +1 -3
  53. data/lib/karafka/version.rb +1 -1
  54. data.tar.gz.sig +0 -0
  55. metadata +19 -7
  56. metadata.gz.sig +0 -0
  57. data/lib/karafka/pro/scheduler.rb +0 -54
  58. data/lib/karafka/scheduler.rb +0 -20
@@ -30,11 +30,25 @@ module Karafka
30
30
 
31
31
  # We validate internals just to be sure, that they are present and working
32
32
  required(:internal).schema do
33
- required(:routing_builder)
34
33
  required(:status)
35
34
  required(:process)
36
- required(:scheduler)
37
- required(:subscription_groups_builder)
35
+
36
+ required(:routing).schema do
37
+ required(:builder)
38
+ required(:subscription_groups_builder)
39
+ end
40
+
41
+ required(:processing).schema do
42
+ required(:jobs_builder)
43
+ required(:scheduler)
44
+ required(:coordinator_class)
45
+ end
46
+
47
+ required(:active_job).schema do
48
+ required(:dispatcher)
49
+ required(:job_options_contract)
50
+ required(:consumer_class)
51
+ end
38
52
  end
39
53
  end
40
54
 
@@ -12,7 +12,7 @@ module Karafka
12
12
  # If there were no consumer_groups declared in the server cli, it means that we will
13
13
  # run all of them and no need to validate them here at all
14
14
  if !value.nil? &&
15
- !(value - Karafka::App.config.internal.routing_builder.map(&:name)).empty?
15
+ !(value - Karafka::App.config.internal.routing.builder.map(&:name)).empty?
16
16
  key(:consumer_groups).failure(:consumer_groups_inclusion)
17
17
  end
18
18
  end
@@ -47,5 +47,8 @@ module Karafka
47
47
  # Used to instrument this error into the error notifications
48
48
  # We do not raise it so we won't crash deployed systems
49
49
  ExpiredLicenseTokenError = Class.new(BaseError)
50
+
51
+ # This should never happen. Please open an issue if it does.
52
+ InvalidCoordinatorState = Class.new(BaseError)
50
53
  end
51
54
  end
@@ -15,16 +15,43 @@ module Karafka
15
15
 
16
16
  # Logs each messages fetching attempt
17
17
  #
18
- # @param _event [Dry::Events::Event] event details including payload
19
- def on_connection_listener_fetch_loop(_event)
20
- info 'Receiving new messages from Kafka...'
18
+ # @param event [Dry::Events::Event] event details including payload
19
+ def on_connection_listener_fetch_loop(event)
20
+ listener = event[:caller]
21
+ info "[#{listener.id}] Polling messages..."
21
22
  end
22
23
 
23
24
  # Logs about messages that we've received from Kafka
24
25
  #
25
26
  # @param event [Dry::Events::Event] event details including payload
26
27
  def on_connection_listener_fetch_loop_received(event)
27
- info "Received #{event[:messages_buffer].size} new messages from Kafka"
28
+ listener = event[:caller]
29
+ time = event[:time]
30
+ messages_count = event[:messages_buffer].size
31
+ info "[#{listener.id}] Polled #{messages_count} messages in #{time}ms"
32
+ end
33
+
34
+ # Prints info about the fact that a given job has started
35
+ #
36
+ # @param event [Dry::Events::Event] event details including payload
37
+ def on_worker_process(event)
38
+ job = event[:job]
39
+ job_type = job.class.to_s.split('::').last
40
+ consumer = job.executor.topic.consumer
41
+ topic = job.executor.topic.name
42
+ info "[#{job.id}] #{job_type} job for #{consumer} on #{topic} started"
43
+ end
44
+
45
+ # Prints info about the fact that a given job has finished
46
+ #
47
+ # @param event [Dry::Events::Event] event details including payload
48
+ def on_worker_processed(event)
49
+ job = event[:job]
50
+ time = event[:time]
51
+ job_type = job.class.to_s.split('::').last
52
+ consumer = job.executor.topic.consumer
53
+ topic = job.executor.topic.name
54
+ info "[#{job.id}] #{job_type} job for #{consumer} on #{topic} finished in #{time}ms"
28
55
  end
29
56
 
30
57
  # Logs info about system signals that Karafka received.
@@ -52,16 +79,14 @@ module Karafka
52
79
  #
53
80
  # @param _event [Dry::Events::Event] event details including payload
54
81
  def on_app_stopping(_event)
55
- # We use a separate thread as logging can't be called from trap context
56
- Thread.new { info 'Stopping Karafka server' }
82
+ info 'Stopping Karafka server'
57
83
  end
58
84
 
59
85
  # Logs info that we stopped the Karafka server.
60
86
  #
61
87
  # @param _event [Dry::Events::Event] event details including payload
62
88
  def on_app_stopped(_event)
63
- # We use a separate thread as logging can't be called from trap context
64
- Thread.new { info 'Stopped Karafka server' }
89
+ info 'Stopped Karafka server'
65
90
  end
66
91
 
67
92
  # There are many types of errors that can occur in many places, but we provide a single
@@ -95,8 +120,7 @@ module Karafka
95
120
  fatal "Runner crashed due to an error: #{error}"
96
121
  fatal details
97
122
  when 'app.stopping.error'
98
- # We use a separate thread as logging can't be called from trap context
99
- Thread.new { error 'Forceful Karafka server stop' }
123
+ error 'Forceful Karafka server stop'
100
124
  when 'librdkafka.error'
101
125
  error "librdkafka internal error occurred: #{error}"
102
126
  error details
@@ -22,7 +22,6 @@ module Karafka
22
22
  app.stopping
23
23
  app.stopped
24
24
 
25
- consumer.prepared
26
25
  consumer.consumed
27
26
  consumer.revoked
28
27
  consumer.shutdown
@@ -33,6 +32,9 @@ module Karafka
33
32
  connection.listener.fetch_loop
34
33
  connection.listener.fetch_loop.received
35
34
 
35
+ worker.process
36
+ worker.processed
37
+
36
38
  statistics.emitted
37
39
 
38
40
  error.occurred
@@ -33,6 +33,8 @@ module Karafka
33
33
 
34
34
  return if license_config.expires_on > Date.today
35
35
 
36
+ raise_expired_license_token_in_dev(license_config.expires_on)
37
+
36
38
  notify_if_license_expired(license_config.expires_on)
37
39
  end
38
40
 
@@ -53,24 +55,41 @@ module Karafka
53
55
  )
54
56
  end
55
57
 
58
+ # Raises an error for test and dev environments if running pro with expired license
59
+ # We never want to cause any non-dev problems and we should never crash anything else than
60
+ # tests and development envs.
61
+ #
62
+ # @param expires_on [Date] when the license expires
63
+ def raise_expired_license_token_in_dev(expires_on)
64
+ env = Karafka::App.env
65
+
66
+ return unless env.development? || env.test?
67
+
68
+ raise Errors::ExpiredLicenseTokenError.new, expired_message(expires_on)
69
+ end
70
+
56
71
  # We do not raise an error here as we don't want to cause any problems to someone that runs
57
72
  # Karafka on production. Error message is enough.
58
73
  #
59
74
  # @param expires_on [Date] when the license expires
60
75
  def notify_if_license_expired(expires_on)
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)
76
+ Karafka.logger.error(expired_message(expires_on))
67
77
 
68
78
  Karafka.monitor.instrument(
69
79
  'error.occurred',
70
80
  caller: self,
71
- error: Errors::ExpiredLicenseTokenError.new(message),
81
+ error: Errors::ExpiredLicenseTokenError.new(expired_message(expires_on)),
72
82
  type: 'licenser.expired'
73
83
  )
74
84
  end
85
+
86
+ # @param expires_on [Date] when the license expires
87
+ # @return [String] expired message
88
+ def expired_message(expires_on)
89
+ <<~MSG.tr("\n", ' ')
90
+ Your license expired on #{expires_on}.
91
+ Please reach us at contact@karafka.io or visit https://karafka.io to obtain a valid one.
92
+ MSG
93
+ end
75
94
  end
76
95
  end
@@ -1,20 +1,41 @@
1
1
  # frozen_string_literal: true
2
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
5
+ # repository 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
+
3
12
  module Karafka
4
13
  module Pro
5
14
  module ActiveJob
6
- # This Karafka component is a Pro component.
7
- # All of the commercial components are present in the lib/karafka/pro directory of this
8
- # repository and their usage requires commercial license agreement.
15
+ # Pro ActiveJob consumer that is suppose to handle long-running jobs as well as short
16
+ # running jobs
9
17
  #
10
- # Karafka has also commercial-friendly license, commercial support and commercial components.
18
+ # When in LRJ, it will pause a given partition forever and will resume its processing only
19
+ # when all the jobs are done processing.
11
20
  #
12
- # By sending a pull request to the pro components, you are agreeing to transfer the copyright
13
- # of your code to Maciej Mensfeld.
21
+ # It contains slightly better revocation warranties than the regular blocking consumer as
22
+ # it can stop processing batch of jobs in the middle after the revocation.
23
+ class Consumer < Karafka::Pro::BaseConsumer
24
+ # Runs ActiveJob jobs processing and handles lrj if needed
25
+ def consume
26
+ messages.each do |message|
27
+ # If for any reason we've lost this partition, not worth iterating over new messages
28
+ # as they are no longer ours
29
+ break if revoked?
30
+ break if Karafka::App.stopping?
14
31
 
15
- # Pro ActiveJob consumer that is suppose to handle long-running jobs as well as short
16
- # running jobs
17
- class Consumer < Karafka::ActiveJob::Consumer
32
+ ::ActiveJob::Base.execute(
33
+ ::ActiveSupport::JSON.decode(message.raw_payload)
34
+ )
35
+
36
+ mark_as_consumed(message)
37
+ end
38
+ end
18
39
  end
19
40
  end
20
41
  end
@@ -1,18 +1,18 @@
1
1
  # frozen_string_literal: true
2
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
5
+ # repository 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
+
3
12
  module Karafka
4
13
  module Pro
5
14
  # Karafka Pro ActiveJob components
6
15
  module ActiveJob
7
- # This Karafka component is a Pro component.
8
- # All of the commercial components are present in the lib/karafka/pro directory of this
9
- # repository and their usage requires commercial license agreement.
10
- #
11
- # Karafka has also commercial-friendly license, commercial support and commercial components.
12
- #
13
- # By sending a pull request to the pro components, you are agreeing to transfer the copyright
14
- # of your code to Maciej Mensfeld.
15
-
16
16
  # Pro dispatcher that sends the ActiveJob job to a proper topic based on the queue name
17
17
  # and that allows to inject additional options into the producer, effectively allowing for a
18
18
  # much better and more granular control over the dispatch and consumption process.
@@ -1,17 +1,17 @@
1
1
  # frozen_string_literal: true
2
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
5
+ # repository 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
+
3
12
  module Karafka
4
13
  module Pro
5
14
  module ActiveJob
6
- # This Karafka component is a Pro component.
7
- # All of the commercial components are present in the lib/karafka/pro directory of this
8
- # repository and their usage requires commercial license agreement.
9
- #
10
- # Karafka has also commercial-friendly license, commercial support and commercial components.
11
- #
12
- # By sending a pull request to the pro components, you are agreeing to transfer the copyright
13
- # of your code to Maciej Mensfeld.
14
-
15
15
  # Contract for validating the options that can be altered with `#karafka_options` per job
16
16
  # class that works with Pro features.
17
17
  class JobOptionsContract < ::Karafka::ActiveJob::JobOptionsContract
@@ -0,0 +1,73 @@
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
5
+ # repository 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 consumer.
15
+ #
16
+ # If you use PRO, all your consumers should inherit (indirectly) from it.
17
+ #
18
+ # @note In case of using lrj, manual pausing may not be the best idea as resume needs to happen
19
+ # after each batch is processed.
20
+ class BaseConsumer < Karafka::BaseConsumer
21
+ # Pause for tops 31 years
22
+ MAX_PAUSE_TIME = 1_000_000_000_000
23
+
24
+ private_constant :MAX_PAUSE_TIME
25
+
26
+ # Pauses processing of a given partition until we're done with the processing
27
+ # This ensures, that we can easily poll not reaching the `max.poll.interval`
28
+ def on_before_consume
29
+ # Pause at the first message in a batch. That way in case of a crash, we will not loose
30
+ # any messages
31
+ return unless topic.long_running_job?
32
+
33
+ pause(messages.first.offset, MAX_PAUSE_TIME)
34
+ end
35
+
36
+ # Runs extra logic after consumption that is related to handling long running jobs
37
+ # @note This overwrites the '#on_after_consume' from the base consumer
38
+ def on_after_consume
39
+ # Nothing to do if we lost the partition
40
+ return if revoked?
41
+
42
+ if @coordinator.success?
43
+ coordinator.pause_tracker.reset
44
+
45
+ # We use the non-blocking one here. If someone needs the blocking one, can implement it
46
+ # with manual offset management
47
+ # Mark as consumed only if manual offset management is not on
48
+ mark_as_consumed(messages.last) unless topic.manual_offset_management?
49
+
50
+ # We check it twice as marking could change this state
51
+ return if revoked?
52
+
53
+ # If this is not a long running job there is nothing for us to do here
54
+ return unless topic.long_running_job?
55
+
56
+ # Once processing is done, we move to the new offset based on commits
57
+ # Here, in case manual offset management is off, we have the new proper offset of a
58
+ # first message from another batch from `@seek_offset`. If manual offset management
59
+ # is on, we move to place where the user indicated it was finished. This can create an
60
+ # interesting (yet valid) corner case, where with manual offset management on and no
61
+ # marking as consumed, we end up with an infinite loop processing same messages over and
62
+ # over again
63
+ seek(@seek_offset || messages.first.offset)
64
+
65
+ resume
66
+ else
67
+ # If processing failed, we need to pause
68
+ pause(@seek_offset || messages.first.offset)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,34 +1,52 @@
1
1
  # frozen_string_literal: true
2
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
5
+ # repository 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
+
3
12
  module Karafka
4
13
  module Pro
5
- # This Karafka component is a Pro component.
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
12
- # of your code to Maciej Mensfeld.
13
-
14
14
  # Loader requires and loads all the pro components only when they are needed
15
15
  class Loader
16
+ # All the pro components that need to be loaded
17
+ COMPONENTS = %w[
18
+ base_consumer
19
+ performance_tracker
20
+ processing/scheduler
21
+ processing/jobs/consume_non_blocking
22
+ processing/jobs_builder
23
+ processing/coordinator
24
+ routing/extensions
25
+ active_job/consumer
26
+ active_job/dispatcher
27
+ active_job/job_options_contract
28
+ ].freeze
29
+
30
+ private_constant :COMPONENTS
31
+
16
32
  class << self
17
33
  # Loads all the pro components and configures them wherever it is expected
18
34
  # @param config [Dry::Configurable::Config] whole app config that we can alter with pro
19
35
  # components
20
36
  def setup(config)
21
- require_relative 'performance_tracker'
22
- require_relative 'scheduler'
23
- require_relative 'processing/jobs/consume_non_blocking'
24
- require_relative 'active_job/consumer'
25
- require_relative 'active_job/dispatcher'
26
- require_relative 'active_job/job_options_contract'
27
-
28
- config.internal.scheduler = Scheduler.new
29
- config.internal.active_job.consumer = ActiveJob::Consumer
30
- config.internal.active_job.dispatcher = ActiveJob::Dispatcher.new
31
- config.internal.active_job.job_options_contract = ActiveJob::JobOptionsContract.new
37
+ COMPONENTS.each { |component| require_relative(component) }
38
+
39
+ icfg = config.internal
40
+
41
+ icfg.processing.coordinator_class = Processing::Coordinator
42
+ icfg.processing.scheduler = Processing::Scheduler.new
43
+ icfg.processing.jobs_builder = Processing::JobsBuilder.new
44
+
45
+ icfg.active_job.consumer_class = ActiveJob::Consumer
46
+ icfg.active_job.dispatcher = ActiveJob::Dispatcher.new
47
+ icfg.active_job.job_options_contract = ActiveJob::JobOptionsContract.new
48
+
49
+ ::Karafka::Routing::Topic.include(Routing::Extensions)
32
50
 
33
51
  config.monitor.subscribe(PerformanceTracker.instance)
34
52
  end
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
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
5
+ # repository 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
+
3
12
  module Karafka
4
13
  module Pro
5
- # This Karafka component is a Pro component.
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
12
- # of your code to Maciej Mensfeld.
13
-
14
14
  # Tracker used to keep track of performance metrics
15
15
  # It provides insights that can be used to optimize processing flow
16
16
  class PerformanceTracker
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Pro
5
+ module Processing
6
+ # Pro coordinator that provides extra orchestration methods useful for parallel processing
7
+ # within the same partition
8
+ class Coordinator < ::Karafka::Processing::Coordinator
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,21 +1,20 @@
1
1
  # frozen_string_literal: true
2
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
5
+ # repository 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
+
3
12
  module Karafka
4
13
  module Pro
5
14
  # Pro components related to processing part of Karafka
6
15
  module Processing
7
16
  # Pro jobs
8
17
  module Jobs
9
- # This Karafka component is a Pro component.
10
- # All of the commercial components are present in the lib/karafka/pro directory of this
11
- # repository and their usage requires commercial license agreement.
12
- #
13
- # Karafka has also commercial-friendly license, commercial support and commercial
14
- # components.
15
- #
16
- # By sending a pull request to the pro components, you are agreeing to transfer the
17
- # copyright of your code to Maciej Mensfeld.
18
-
19
18
  # The main job type in a non-blocking variant.
20
19
  # This variant works "like" the regular consumption but pauses the partition for as long
21
20
  # as it is needed until a job is done.
@@ -27,7 +26,7 @@ module Karafka
27
26
  # management. This layer of the framework knows nothing about Kafka messages consumption.
28
27
  class ConsumeNonBlocking < ::Karafka::Processing::Jobs::Consume
29
28
  # Releases the blocking lock after it is done with the preparation phase for this job
30
- def prepare
29
+ def before_call
31
30
  super
32
31
  @non_blocking = true
33
32
  end
@@ -0,0 +1,32 @@
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
5
+ # repository 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 Processing
15
+ # Pro jobs builder that supports lrj
16
+ class JobsBuilder < ::Karafka::Processing::JobsBuilder
17
+ # @param executor [Karafka::Processing::Executor]
18
+ # @param messages [Karafka::Messages::Messages] messages batch to be consumed
19
+ # @param coordinator [Karafka::Processing::Coordinator]
20
+ # @return [Karafka::Processing::Jobs::Consume] blocking job
21
+ # @return [Karafka::Pro::Processing::Jobs::ConsumeNonBlocking] non blocking for lrj
22
+ def consume(executor, messages, coordinator)
23
+ if executor.topic.long_running_job?
24
+ Jobs::ConsumeNonBlocking.new(executor, messages, coordinator)
25
+ else
26
+ super
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,56 @@
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
5
+ # repository 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 Processing
15
+ # Optimizes scheduler that takes into consideration of execution time needed to process
16
+ # messages from given topics partitions. It uses the non-preemptive LJF algorithm
17
+ #
18
+ # This scheduler is designed to optimize execution times on jobs that perform IO operations
19
+ # as when taking IO into consideration, the can achieve optimized parallel processing.
20
+ #
21
+ # This scheduler can also work with virtual partitions.
22
+ #
23
+ # Aside from consumption jobs, other jobs do not run often, thus we can leave them with
24
+ # default FIFO scheduler from the default Karafka scheduler
25
+ class Scheduler < ::Karafka::Processing::Scheduler
26
+ # Schedules jobs in the LJF order for consumption
27
+ #
28
+ # @param queue [Karafka::Processing::JobsQueue] queue where we want to put the jobs
29
+ # @param jobs_array [Array<Karafka::Processing::Jobs::Base>] jobs we want to schedule
30
+ #
31
+ def schedule_consumption(queue, jobs_array)
32
+ pt = PerformanceTracker.instance
33
+
34
+ ordered = []
35
+
36
+ jobs_array.each do |job|
37
+ messages = job.messages
38
+ message = messages.first
39
+
40
+ cost = pt.processing_time_p95(message.topic, message.partition) * messages.size
41
+
42
+ ordered << [job, cost]
43
+ end
44
+
45
+ ordered.sort_by!(&:last)
46
+ ordered.reverse!
47
+ ordered.map!(&:first)
48
+
49
+ ordered.each do |job|
50
+ queue << job
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,32 @@
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
5
+ # repository 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
+ # Pro routing components
15
+ module Routing
16
+ # Routing extensions that allow to configure some extra PRO routing options
17
+ module Extensions
18
+ class << self
19
+ # @param base [Class] class we extend
20
+ def included(base)
21
+ base.attr_accessor :long_running_job
22
+ end
23
+ end
24
+
25
+ # @return [Boolean] is a given job on a topic a long running one
26
+ def long_running_job?
27
+ @long_running_job || false
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end