karafka 2.0.0.beta1 → 2.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/ci.yml +9 -23
  4. data/CHANGELOG.md +47 -0
  5. data/Gemfile.lock +8 -8
  6. data/bin/integrations +36 -14
  7. data/bin/scenario +29 -0
  8. data/bin/wait_for_kafka +20 -0
  9. data/config/errors.yml +1 -0
  10. data/docker-compose.yml +12 -0
  11. data/karafka.gemspec +2 -2
  12. data/lib/active_job/karafka.rb +2 -2
  13. data/lib/karafka/active_job/routing/extensions.rb +31 -0
  14. data/lib/karafka/base_consumer.rb +65 -42
  15. data/lib/karafka/connection/client.rb +65 -19
  16. data/lib/karafka/connection/listener.rb +99 -34
  17. data/lib/karafka/connection/listeners_batch.rb +24 -0
  18. data/lib/karafka/connection/messages_buffer.rb +50 -54
  19. data/lib/karafka/connection/raw_messages_buffer.rb +101 -0
  20. data/lib/karafka/contracts/config.rb +9 -1
  21. data/lib/karafka/helpers/async.rb +33 -0
  22. data/lib/karafka/instrumentation/logger_listener.rb +34 -10
  23. data/lib/karafka/instrumentation/monitor.rb +3 -1
  24. data/lib/karafka/licenser.rb +26 -7
  25. data/lib/karafka/messages/batch_metadata.rb +26 -3
  26. data/lib/karafka/messages/builders/batch_metadata.rb +17 -29
  27. data/lib/karafka/messages/builders/message.rb +1 -0
  28. data/lib/karafka/messages/builders/messages.rb +4 -12
  29. data/lib/karafka/pro/active_job/consumer.rb +49 -0
  30. data/lib/karafka/pro/active_job/dispatcher.rb +10 -10
  31. data/lib/karafka/pro/active_job/job_options_contract.rb +9 -9
  32. data/lib/karafka/pro/base_consumer.rb +76 -0
  33. data/lib/karafka/pro/loader.rb +30 -13
  34. data/lib/karafka/pro/performance_tracker.rb +9 -9
  35. data/lib/karafka/pro/processing/jobs/consume_non_blocking.rb +37 -0
  36. data/lib/karafka/pro/processing/jobs_builder.rb +31 -0
  37. data/lib/karafka/pro/routing/extensions.rb +32 -0
  38. data/lib/karafka/pro/scheduler.rb +54 -0
  39. data/lib/karafka/processing/executor.rb +34 -7
  40. data/lib/karafka/processing/executors_buffer.rb +15 -7
  41. data/lib/karafka/processing/jobs/base.rb +21 -4
  42. data/lib/karafka/processing/jobs/consume.rb +12 -5
  43. data/lib/karafka/processing/jobs_builder.rb +28 -0
  44. data/lib/karafka/processing/jobs_queue.rb +15 -12
  45. data/lib/karafka/processing/result.rb +34 -0
  46. data/lib/karafka/processing/worker.rb +23 -17
  47. data/lib/karafka/processing/workers_batch.rb +5 -0
  48. data/lib/karafka/routing/consumer_group.rb +1 -1
  49. data/lib/karafka/routing/subscription_group.rb +2 -2
  50. data/lib/karafka/routing/subscription_groups_builder.rb +3 -2
  51. data/lib/karafka/routing/topic.rb +5 -0
  52. data/lib/karafka/routing/topics.rb +38 -0
  53. data/lib/karafka/runner.rb +19 -27
  54. data/lib/karafka/scheduler.rb +10 -11
  55. data/lib/karafka/server.rb +24 -23
  56. data/lib/karafka/setup/config.rb +4 -1
  57. data/lib/karafka/status.rb +1 -3
  58. data/lib/karafka/version.rb +1 -1
  59. data.tar.gz.sig +0 -0
  60. metadata +20 -5
  61. metadata.gz.sig +0 -0
  62. data/lib/karafka/active_job/routing_extensions.rb +0 -18
@@ -3,32 +3,37 @@
3
3
  module Karafka
4
4
  # Class used to run the Karafka listeners in separate threads
5
5
  class Runner
6
- # Starts listening on all the listeners asynchronously
7
- # Fetch loop should never end. If they do, it is a critical error
6
+ # Starts listening on all the listeners asynchronously and handles the jobs queue closing
7
+ # after listeners are done with their work.
8
8
  def call
9
9
  # Despite possibility of having several independent listeners, we aim to have one queue for
10
10
  # jobs across and one workers poll for that
11
11
  jobs_queue = Processing::JobsQueue.new
12
12
 
13
13
  workers = Processing::WorkersBatch.new(jobs_queue)
14
- Karafka::Server.workers = workers
14
+ listeners = Connection::ListenersBatch.new(jobs_queue)
15
15
 
16
- threads = listeners(jobs_queue).map do |listener|
17
- # We abort on exception because there should be an exception handling developed for
18
- # each listener running in separate threads, so the exceptions should never leak
19
- # and if that happens, it means that something really bad happened and we should stop
20
- # the whole process
21
- Thread
22
- .new { listener.call }
23
- .tap { |thread| thread.abort_on_exception = true }
24
- end
16
+ workers.each(&:async_call)
17
+ listeners.each(&:async_call)
25
18
 
26
19
  # We aggregate threads here for a supervised shutdown process
27
- Karafka::Server.consumer_threads = threads
20
+ Karafka::Server.workers = workers
21
+ Karafka::Server.listeners = listeners
28
22
 
29
23
  # All the listener threads need to finish
30
- threads.each(&:join)
24
+ listeners.each(&:join)
25
+
26
+ # We close the jobs queue only when no listener threads are working.
27
+ # This ensures, that everything was closed prior to us not accepting anymore jobs and that
28
+ # no more jobs will be enqueued. Since each listener waits for jobs to finish, once those
29
+ # are done, we can close.
30
+ jobs_queue.close
31
+
31
32
  # All the workers need to stop processing anything before we can stop the runner completely
33
+ # This ensures that even async long-running jobs have time to finish before we are done
34
+ # with everything. One thing worth keeping in mind though: It is the end user responsibility
35
+ # to handle the shutdown detection in their long-running processes. Otherwise if timeout
36
+ # is exceeded, there will be a forced shutdown.
32
37
  workers.each(&:join)
33
38
  # If anything crashes here, we need to raise the error and crush the runner because it means
34
39
  # that something terrible happened
@@ -42,18 +47,5 @@ module Karafka
42
47
  Karafka::App.stop!
43
48
  raise e
44
49
  end
45
-
46
- private
47
-
48
- # @param jobs_queue [Processing::JobsQueue] the main processing queue
49
- # @return [Array<Karafka::Connection::Listener>] listeners that will consume messages for each
50
- # of the subscription groups
51
- def listeners(jobs_queue)
52
- App
53
- .subscription_groups
54
- .map do |subscription_group|
55
- Karafka::Connection::Listener.new(subscription_group, jobs_queue)
56
- end
57
- end
58
50
  end
59
51
  end
@@ -3,19 +3,18 @@
3
3
  module Karafka
4
4
  # FIFO scheduler for messages coming from various topics and partitions
5
5
  class Scheduler
6
- # Yields messages from partitions in the fifo order
6
+ # Schedules jobs in the fifo order
7
7
  #
8
- # @param messages_buffer [Karafka::Connection::MessagesBuffer] messages buffer with data from
9
- # multiple topics and partitions
10
- # @yieldparam [String] topic name
11
- # @yieldparam [Integer] partition number
12
- # @yieldparam [Array<Rdkafka::Consumer::Message>] topic partition aggregated results
13
- def call(messages_buffer)
14
- messages_buffer.each do |topic, partitions|
15
- partitions.each do |partition, messages|
16
- yield(topic, partition, messages)
17
- end
8
+ # @param queue [Karafka::Processing::JobsQueue] queue where we want to put the jobs
9
+ # @param jobs_array [Array<Karafka::Processing::Jobs::Base>] jobs we want to schedule
10
+ def schedule_consumption(queue, jobs_array)
11
+ jobs_array.each do |job|
12
+ queue << job
18
13
  end
19
14
  end
15
+
16
+ # Both revocation and shutdown jobs can also run in fifo by default
17
+ alias schedule_revocation schedule_consumption
18
+ alias schedule_shutdown schedule_consumption
20
19
  end
21
20
  end
@@ -15,7 +15,7 @@ module Karafka
15
15
 
16
16
  class << self
17
17
  # Set of consuming threads. Each consumer thread contains a single consumer
18
- attr_accessor :consumer_threads
18
+ attr_accessor :listeners
19
19
 
20
20
  # Set of workers
21
21
  attr_accessor :workers
@@ -25,9 +25,12 @@ module Karafka
25
25
 
26
26
  # Method which runs app
27
27
  def run
28
- process.on_sigint { stop }
29
- process.on_sigquit { stop }
30
- process.on_sigterm { stop }
28
+ # Since we do a lot of threading and queuing, we don't want to stop from the trap context
29
+ # as some things may not work there as expected, that is why we spawn a separate thread to
30
+ # handle the stopping process
31
+ process.on_sigint { Thread.new { stop } }
32
+ process.on_sigquit { Thread.new { stop } }
33
+ process.on_sigterm { Thread.new { stop } }
31
34
 
32
35
  # Start is blocking until stop is called and when we stop, it will wait until
33
36
  # all of the things are ready to stop
@@ -35,6 +38,8 @@ module Karafka
35
38
 
36
39
  # We always need to wait for Karafka to stop here since we should wait for the stop running
37
40
  # in a separate thread (or trap context) to indicate everything is closed
41
+ # Since `#start` is blocking, we were get here only after the runner is done. This will
42
+ # not add any performance degradation because of that.
38
43
  Thread.pass until Karafka::App.stopped?
39
44
  # Try its best to shutdown underlying components before re-raising
40
45
  # rubocop:disable Lint/RescueException
@@ -70,16 +75,16 @@ module Karafka
70
75
  def stop
71
76
  Karafka::App.stop!
72
77
 
73
- timeout = Thread.new { Karafka::App.config.shutdown_timeout }.join.value
78
+ timeout = Karafka::App.config.shutdown_timeout
74
79
 
75
80
  # We check from time to time (for the timeout period) if all the threads finished
76
81
  # their work and if so, we can just return and normal shutdown process will take place
77
82
  # We divide it by 1000 because we use time in ms.
78
83
  ((timeout / 1_000) * SUPERVISION_CHECK_FACTOR).to_i.times do
79
- if consumer_threads.count(&:alive?).zero? &&
84
+ if listeners.count(&:alive?).zero? &&
80
85
  workers.count(&:alive?).zero?
81
86
 
82
- Thread.new { Karafka::App.producer.close }.join
87
+ Karafka::App.producer.close
83
88
 
84
89
  return
85
90
  end
@@ -89,22 +94,18 @@ module Karafka
89
94
 
90
95
  raise Errors::ForcefulShutdownError
91
96
  rescue Errors::ForcefulShutdownError => e
92
- thread = Thread.new do
93
- Karafka.monitor.instrument(
94
- 'error.occurred',
95
- caller: self,
96
- error: e,
97
- type: 'app.stopping.error'
98
- )
99
-
100
- # We're done waiting, lets kill them!
101
- workers.each(&:terminate)
102
- consumer_threads.each(&:terminate)
103
-
104
- Karafka::App.producer.close
105
- end
106
-
107
- thread.join
97
+ Karafka.monitor.instrument(
98
+ 'error.occurred',
99
+ caller: self,
100
+ error: e,
101
+ type: 'app.stopping.error'
102
+ )
103
+
104
+ # We're done waiting, lets kill them!
105
+ workers.each(&:terminate)
106
+ listeners.each(&:terminate)
107
+
108
+ Karafka::App.producer.close
108
109
 
109
110
  # exit! is not within the instrumentation as it would not trigger due to exit
110
111
  Kernel.exit! FORCEFUL_EXIT_CODE
@@ -83,7 +83,7 @@ module Karafka
83
83
  # @see https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md
84
84
  setting :kafka, default: {}
85
85
 
86
- # Namespace for internal settings that should not be modified
86
+ # Namespace for internal settings that should not be modified directly
87
87
  setting :internal do
88
88
  # option routing_builder [Karafka::Routing::Builder] builder instance
89
89
  setting :routing_builder, default: Routing::Builder.new
@@ -98,6 +98,8 @@ module Karafka
98
98
  setting :subscription_groups_builder, default: Routing::SubscriptionGroupsBuilder.new
99
99
  # option scheduler [Class] scheduler we will be using
100
100
  setting :scheduler, default: Scheduler.new
101
+ # option jobs_builder [Class] jobs builder we want to use
102
+ setting :jobs_builder, default: Processing::JobsBuilder.new
101
103
 
102
104
  # Karafka components for ActiveJob
103
105
  setting :active_job do
@@ -117,6 +119,7 @@ module Karafka
117
119
  def setup(&block)
118
120
  configure(&block)
119
121
  merge_kafka_defaults!(config)
122
+
120
123
  Contracts::Config.new.validate!(config.to_h)
121
124
 
122
125
  # Check the license presence (if needed) and
@@ -31,9 +31,7 @@ module Karafka
31
31
  # We skip as during this state we do not have yet a monitor
32
32
  return if initializing?
33
33
 
34
- # Trap context disallows to run certain things that we instrument
35
- # so the state changes are executed from a separate thread
36
- Thread.new { Karafka.monitor.instrument("app.#{state}") }.join
34
+ Karafka.monitor.instrument("app.#{state}")
37
35
  end
38
36
  end
39
37
  end
@@ -3,5 +3,5 @@
3
3
  # Main module namespace
4
4
  module Karafka
5
5
  # Current Karafka version
6
- VERSION = '2.0.0.beta1'
6
+ VERSION = '2.0.0.beta4'
7
7
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: karafka
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.beta1
4
+ version: 2.0.0.beta4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Mensfeld
@@ -34,7 +34,7 @@ cert_chain:
34
34
  R2P11bWoCtr70BsccVrN8jEhzwXngMyI2gVt750Y+dbTu1KgRqZKp/ECe7ZzPzXj
35
35
  pIy9vHxTANKYVyI4qj8OrFdEM5BQNu8oQpL0iQ==
36
36
  -----END CERTIFICATE-----
37
- date: 2022-05-22 00:00:00.000000000 Z
37
+ date: 2022-06-20 00:00:00.000000000 Z
38
38
  dependencies:
39
39
  - !ruby/object:Gem::Dependency
40
40
  name: dry-configurable
@@ -112,7 +112,7 @@ dependencies:
112
112
  requirements:
113
113
  - - ">="
114
114
  - !ruby/object:Gem::Version
115
- version: 2.3.0
115
+ version: 2.3.1
116
116
  - - "<"
117
117
  - !ruby/object:Gem::Version
118
118
  version: 3.0.0
@@ -122,7 +122,7 @@ dependencies:
122
122
  requirements:
123
123
  - - ">="
124
124
  - !ruby/object:Gem::Version
125
- version: 2.3.0
125
+ version: 2.3.1
126
126
  - - "<"
127
127
  - !ruby/object:Gem::Version
128
128
  version: 3.0.0
@@ -171,7 +171,9 @@ files:
171
171
  - bin/create_token
172
172
  - bin/integrations
173
173
  - bin/karafka
174
+ - bin/scenario
174
175
  - bin/stress
176
+ - bin/wait_for_kafka
175
177
  - certs/karafka-pro.pem
176
178
  - certs/mensfeld.pem
177
179
  - config/errors.yml
@@ -184,7 +186,7 @@ files:
184
186
  - lib/karafka/active_job/dispatcher.rb
185
187
  - lib/karafka/active_job/job_extensions.rb
186
188
  - lib/karafka/active_job/job_options_contract.rb
187
- - lib/karafka/active_job/routing_extensions.rb
189
+ - lib/karafka/active_job/routing/extensions.rb
188
190
  - lib/karafka/app.rb
189
191
  - lib/karafka/base_consumer.rb
190
192
  - lib/karafka/cli.rb
@@ -195,8 +197,10 @@ files:
195
197
  - lib/karafka/cli/server.rb
196
198
  - lib/karafka/connection/client.rb
197
199
  - lib/karafka/connection/listener.rb
200
+ - lib/karafka/connection/listeners_batch.rb
198
201
  - lib/karafka/connection/messages_buffer.rb
199
202
  - lib/karafka/connection/pauses_manager.rb
203
+ - lib/karafka/connection/raw_messages_buffer.rb
200
204
  - lib/karafka/connection/rebalance_manager.rb
201
205
  - lib/karafka/contracts.rb
202
206
  - lib/karafka/contracts/base.rb
@@ -206,6 +210,7 @@ files:
206
210
  - lib/karafka/contracts/server_cli_options.rb
207
211
  - lib/karafka/env.rb
208
212
  - lib/karafka/errors.rb
213
+ - lib/karafka/helpers/async.rb
209
214
  - lib/karafka/helpers/multi_delegator.rb
210
215
  - lib/karafka/instrumentation.rb
211
216
  - lib/karafka/instrumentation/callbacks/error.rb
@@ -225,10 +230,16 @@ files:
225
230
  - lib/karafka/messages/seek.rb
226
231
  - lib/karafka/patches/rdkafka/consumer.rb
227
232
  - lib/karafka/pro.rb
233
+ - lib/karafka/pro/active_job/consumer.rb
228
234
  - lib/karafka/pro/active_job/dispatcher.rb
229
235
  - lib/karafka/pro/active_job/job_options_contract.rb
236
+ - lib/karafka/pro/base_consumer.rb
230
237
  - lib/karafka/pro/loader.rb
231
238
  - lib/karafka/pro/performance_tracker.rb
239
+ - lib/karafka/pro/processing/jobs/consume_non_blocking.rb
240
+ - lib/karafka/pro/processing/jobs_builder.rb
241
+ - lib/karafka/pro/routing/extensions.rb
242
+ - lib/karafka/pro/scheduler.rb
232
243
  - lib/karafka/process.rb
233
244
  - lib/karafka/processing/executor.rb
234
245
  - lib/karafka/processing/executors_buffer.rb
@@ -236,7 +247,9 @@ files:
236
247
  - lib/karafka/processing/jobs/consume.rb
237
248
  - lib/karafka/processing/jobs/revoked.rb
238
249
  - lib/karafka/processing/jobs/shutdown.rb
250
+ - lib/karafka/processing/jobs_builder.rb
239
251
  - lib/karafka/processing/jobs_queue.rb
252
+ - lib/karafka/processing/result.rb
240
253
  - lib/karafka/processing/worker.rb
241
254
  - lib/karafka/processing/workers_batch.rb
242
255
  - lib/karafka/railtie.rb
@@ -248,6 +261,7 @@ files:
248
261
  - lib/karafka/routing/subscription_group.rb
249
262
  - lib/karafka/routing/subscription_groups_builder.rb
250
263
  - lib/karafka/routing/topic.rb
264
+ - lib/karafka/routing/topics.rb
251
265
  - lib/karafka/runner.rb
252
266
  - lib/karafka/scheduler.rb
253
267
  - lib/karafka/serialization/json/deserializer.rb
@@ -266,6 +280,7 @@ files:
266
280
  homepage: https://karafka.io
267
281
  licenses:
268
282
  - LGPL-3.0
283
+ - Commercial
269
284
  metadata:
270
285
  source_code_uri: https://github.com/karafka/karafka
271
286
  rubygems_mfa_required: 'true'
metadata.gz.sig CHANGED
Binary file
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Karafka
4
- # ActiveJob related Karafka stuff
5
- module ActiveJob
6
- # Routing extensions for ActiveJob
7
- module RoutingExtensions
8
- # This method simplifies routes definition for ActiveJob topics / queues by auto-injecting
9
- # the consumer class
10
- # @param name [String, Symbol] name of the topic where ActiveJobs jobs should go
11
- def active_job_topic(name)
12
- topic(name) do
13
- consumer App.config.internal.active_job.consumer
14
- end
15
- end
16
- end
17
- end
18
- end