karafka 2.0.0.beta2 → 2.0.0.beta5

Sign up to get free protection for your applications and to get access to all the features.
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