karafka 2.4.6 → 2.4.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e550fa0b7395eab3b961181319d195809baeca82c02935d2735cf98353f6ef8b
4
- data.tar.gz: f734562bfb86db59a64f4fbeb596bfe126eacd428a4ade00da90d238d959ab64
3
+ metadata.gz: 6c94c3a4e646233f535e63bcf7a65c88d656f534bfccd21cad87be59b525adb1
4
+ data.tar.gz: 63b6ad0491880325d6ac0e5a7367f2a938e8a36245bd1cff48d3de73da6266c6
5
5
  SHA512:
6
- metadata.gz: f80b521d063653c93caee987358573c992af2488ffec9c6465dbb04f9e51e8334548cf669b2b5f8f5783540be01fa8b52e067042cf72afda35f14181148ec87d
7
- data.tar.gz: 32fd0f08102a2687f4b646d15cd07599f9aaefac957f828396af1f2d91bc91d5c922119bfec04d94cba63af86724d43cb50852de140d78e289fdbb612892dc78
6
+ metadata.gz: 6c04bcd1f7ed17855140e519a82a52c0cae93a3c41126120ab797bbcf172315f7f7720f12e233cc85eed4af2dd437a3364f7f5613f730c9b2e32f8b79225570f
7
+ data.tar.gz: 3441c4ac2230c5903dacd210b54c80c05f72c2804490229334d10656672c4ef9d9b3160c12cdd4d22916d55f1687a730980b0b3fc21099a003f36238f58025eb
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Karafka Framework Changelog
2
2
 
3
+ ## 2.4.7 (2024-08-01)
4
+ - [Enhancement] Introduce `Karafka::Server.mode` to check in what mode Karafka process operates (`standalone`, `swarm`, `supervisor`, `embedded`).
5
+ - [Enhancement] Ensure `max.poll.interval.ms` is always present and populate it with librdkafka default.
6
+ - [Enhancement] Introduce a shutdown time limit for unsubscription wait.
7
+ - [Enhancement] Tag with `mode:swarm` each of the running swarm consumers.
8
+ - [Change] Tag with `mode:embedded` instead of `embedded` the embedded consumers.
9
+ - [Fix] License identifier `LGPL-3.0` is deprecated for SPDX (#2177).
10
+ - [Fix] Fix an issue where custom clusters would not have default settings populated same as the primary cluster.
11
+ - [Fix] Fix Rspec warnings of nil mocks.
12
+ - [Maintenance] Cover `cooperative-sticky` librdkafka issues with integration spec.
13
+
3
14
  ## 2.4.6 (2024-07-22)
4
15
  - [Fix] Mitigate `rd_kafka_cgrp_terminated` and other `librdkafka` shutdown issues by unsubscribing fully prior to shutdown.
5
16
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka (2.4.6)
4
+ karafka (2.4.7)
5
5
  base64 (~> 0.2)
6
6
  karafka-core (>= 2.4.3, < 2.5.0)
7
7
  waterdrop (>= 2.7.3, < 3.0.0)
@@ -41,11 +41,11 @@ GEM
41
41
  concurrent-ruby (~> 1.0)
42
42
  karafka-core (2.4.4)
43
43
  karafka-rdkafka (>= 0.15.0, < 0.18.0)
44
- karafka-rdkafka (0.17.0)
44
+ karafka-rdkafka (0.17.1)
45
45
  ffi (~> 1.15)
46
46
  mini_portile2 (~> 2.6)
47
47
  rake (> 12)
48
- karafka-testing (2.4.5)
48
+ karafka-testing (2.4.6)
49
49
  karafka (>= 2.4.0, < 2.5.0)
50
50
  waterdrop (>= 2.7.0)
51
51
  karafka-web (0.9.1)
@@ -88,7 +88,7 @@ GEM
88
88
  karafka-core (>= 2.4.3, < 3.0.0)
89
89
  karafka-rdkafka (>= 0.15.1)
90
90
  zeitwerk (~> 2.3)
91
- zeitwerk (2.6.16)
91
+ zeitwerk (2.6.17)
92
92
 
93
93
  PLATFORMS
94
94
  ruby
data/LICENSE CHANGED
@@ -1,7 +1,7 @@
1
1
  Copyright (c) Maciej Mensfeld
2
2
 
3
3
  Karafka is an Open Source project licensed under the terms of
4
- the LGPLv3 license. Please see <https://github.com/karafka/karafka/blob/master/LGPL>
4
+ the LGPLv3 license. Please see <https://github.com/karafka/karafka/blob/master/LICENSE-LGPL>
5
5
  for license text.
6
6
 
7
7
  Karafka has also commercial-friendly license, commercial support and commercial components.
data/docker-compose.yml CHANGED
@@ -3,7 +3,7 @@ version: '2'
3
3
  services:
4
4
  kafka:
5
5
  container_name: kafka
6
- image: confluentinc/cp-kafka:7.6.2
6
+ image: confluentinc/cp-kafka:7.7.0
7
7
 
8
8
  ports:
9
9
  - 9092:9092
data/karafka.gemspec CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.authors = ['Maciej Mensfeld']
13
13
  spec.email = %w[contact@karafka.io]
14
14
  spec.homepage = 'https://karafka.io'
15
- spec.licenses = %w[LGPL-3.0 Commercial]
15
+ spec.licenses = %w[LGPL-3.0-only Commercial]
16
16
  spec.summary = 'Karafka is Ruby and Rails efficient Kafka processing framework.'
17
17
  spec.description = <<-DESC
18
18
  Karafka is Ruby and Rails efficient Kafka processing framework.
@@ -89,6 +89,7 @@ module Karafka
89
89
  register_inclusions
90
90
  register_exclusions
91
91
 
92
+ Karafka::Server.execution_mode = :standalone
92
93
  Karafka::Server.run
93
94
  end
94
95
 
@@ -23,6 +23,7 @@ module Karafka
23
23
  server.register_inclusions
24
24
  server.register_exclusions
25
25
 
26
+ Karafka::Server.execution_mode = :supervisor
26
27
  Karafka::Swarm::Supervisor.new.run
27
28
  end
28
29
  end
@@ -8,6 +8,8 @@ module Karafka
8
8
  # It is threadsafe and provides some security measures so we won't end up operating on a
9
9
  # closed consumer instance as it causes Ruby VM process to crash.
10
10
  class Client
11
+ include ::Karafka::Core::Helpers::Time
12
+
11
13
  attr_reader :rebalance_manager
12
14
 
13
15
  # @return [Karafka::Routing::SubscriptionGroup] subscription group to which this client
@@ -24,7 +26,12 @@ module Karafka
24
26
  # How many times should we retry polling in case of a failure
25
27
  MAX_POLL_RETRIES = 20
26
28
 
27
- private_constant :MAX_POLL_RETRIES
29
+ # How much time of the total shutdown time can we wait for our manual unsubscribe before
30
+ # attempting to close without unsubscribe. We try to wait for 50% of the shutdown time
31
+ # before we move to a regular unsubscribe.
32
+ COOP_UNSUBSCRIBE_FACTOR = 0.5
33
+
34
+ private_constant :MAX_POLL_RETRIES, :COOP_UNSUBSCRIBE_FACTOR
28
35
 
29
36
  # Creates a new consumer instance.
30
37
  #
@@ -261,14 +268,32 @@ module Karafka
261
268
  # an issue that gets back every few versions of librdkafka in a limited scope, for example
262
269
  # for cooperative-sticky or in a general scope. This is why we unsubscribe and wait until
263
270
  # we no longer have any assignments. That way librdkafka consumer shutdown should never
264
- # happen with rebalance associated with the given consumer instance
271
+ # happen with rebalance associated with the given consumer instance. Since we do not want
272
+ # to wait forever, we also impose a limit on how long should we wait. This prioritizes
273
+ # shutdown stability over endless wait.
274
+ #
275
+ # The `@unsubscribing` ensures that when there would be a direct close attempt, it
276
+ # won't get into this loop again. This can happen when supervision decides it should close
277
+ # things faster
265
278
  #
266
279
  # @see https://github.com/confluentinc/librdkafka/issues/4792
267
280
  # @see https://github.com/confluentinc/librdkafka/issues/4527
268
281
  if unsubscribe?
282
+ @unsubscribing = true
283
+
284
+ # Give 50% of time for the final close before we reach the forceful
285
+ max_wait = ::Karafka::App.config.shutdown_timeout * COOP_UNSUBSCRIBE_FACTOR
286
+ used = 0
287
+ stopped_at = monotonic_now
288
+
269
289
  unsubscribe
270
290
 
271
291
  until assignment.empty?
292
+ used += monotonic_now - stopped_at
293
+ stopped_at = monotonic_now
294
+
295
+ break if used >= max_wait
296
+
272
297
  sleep(0.1)
273
298
 
274
299
  ping
@@ -709,6 +734,7 @@ module Karafka
709
734
  #
710
735
  # @return [Boolean] should we unsubscribe prior to shutdown
711
736
  def unsubscribe?
737
+ return false if @unsubscribing
712
738
  return false if @subscription_group.kafka.key?(:'group.instance.id')
713
739
  return false if @mode != :subscribe
714
740
  return false if assignment.empty?
@@ -27,7 +27,8 @@ module Karafka
27
27
  Thread.new do
28
28
  Thread.current.name = 'karafka.embedded'
29
29
 
30
- Karafka::Process.tags.add(:execution_mode, 'embedded')
30
+ Karafka::Process.tags.add(:execution_mode, 'mode:embedded')
31
+ Karafka::Server.execution_mode = :embedded
31
32
  Karafka::Server.start
32
33
  end
33
34
  end
@@ -8,6 +8,12 @@ module Karafka
8
8
  # @note A single consumer group represents Kafka consumer group, but it may not match 1:1 with
9
9
  # subscription groups. There can be more subscription groups than consumer groups
10
10
  class ConsumerGroup
11
+ include Helpers::ConfigImporter.new(
12
+ activity_manager: %i[internal routing activity_manager],
13
+ builder: %i[internal routing builder],
14
+ subscription_groups_builder: %i[internal routing subscription_groups_builder]
15
+ )
16
+
11
17
  attr_reader :id, :topics, :name
12
18
 
13
19
  # This is a "virtual" attribute that is not building subscription groups.
@@ -32,7 +38,7 @@ module Karafka
32
38
 
33
39
  # @return [Boolean] true if this consumer group should be active in our current process
34
40
  def active?
35
- config.internal.routing.activity_manager.active?(:consumer_groups, name)
41
+ activity_manager.active?(:consumer_groups, name)
36
42
  end
37
43
 
38
44
  # Builds a topic representation inside of a current consumer group route
@@ -43,7 +49,7 @@ module Karafka
43
49
  topic = Topic.new(name, self)
44
50
  @topics << Proxy.new(
45
51
  topic,
46
- config.internal.routing.builder.defaults,
52
+ builder.defaults,
47
53
  &block
48
54
  ).target
49
55
  built_topic = @topics.last
@@ -72,11 +78,7 @@ module Karafka
72
78
  # @return [Array<Routing::SubscriptionGroup>] all the subscription groups build based on
73
79
  # the consumer group topics
74
80
  def subscription_groups
75
- @subscription_groups ||= config
76
- .internal
77
- .routing
78
- .subscription_groups_builder
79
- .call(topics)
81
+ @subscription_groups ||= subscription_groups_builder.call(topics)
80
82
  end
81
83
 
82
84
  # Hashed version of consumer group that can be used for validation purposes
@@ -88,13 +90,6 @@ module Karafka
88
90
  id: id
89
91
  }.freeze
90
92
  end
91
-
92
- private
93
-
94
- # @return [Karafka::Core::Configurable::Node] root node config
95
- def config
96
- ::Karafka::App.config
97
- end
98
93
  end
99
94
  end
100
95
  end
@@ -120,6 +120,7 @@ module Karafka
120
120
  def build_kafka
121
121
  kafka = Setup::AttributesMap.consumer(@topics.first.kafka.dup)
122
122
 
123
+ inject_defaults(kafka)
123
124
  inject_group_instance_id(kafka)
124
125
  inject_client_id(kafka)
125
126
 
@@ -132,6 +133,13 @@ module Karafka
132
133
  kafka
133
134
  end
134
135
 
136
+ # Injects (if needed) defaults
137
+ #
138
+ # @param kafka [Hash] kafka level config
139
+ def inject_defaults(kafka)
140
+ Setup::DefaultsInjector.consumer(kafka)
141
+ end
142
+
135
143
  # Sets (if needed) the client.id attribute
136
144
  #
137
145
  # @param kafka [Hash] kafka level config
@@ -19,6 +19,17 @@ module Karafka
19
19
  # Jobs queue
20
20
  attr_accessor :jobs_queue
21
21
 
22
+ # Mode in which the Karafka server is executed. It can be:
23
+ #
24
+ # - :standalone - regular karafka consumer process
25
+ # - :embedded - embedded in a different process and not supervised
26
+ # - :supervisor - swarm supervisor process
27
+ # - :swarm - one of swarm processes
28
+ #
29
+ # Sometimes it is important to know in what mode we operate, especially from UI perspective
30
+ # as not everything is possible when operating in non-standalone mode, etc.
31
+ attr_accessor :execution_mode
32
+
22
33
  # Method which runs app
23
34
  def run
24
35
  self.listeners = []
@@ -171,5 +182,10 @@ module Karafka
171
182
  config.internal.process
172
183
  end
173
184
  end
185
+
186
+ # Always start with standalone so there always is a value for the execution mode.
187
+ # This is overwritten quickly during boot, but just in case someone would reach it prior to
188
+ # booting, we want to have the default value.
189
+ self.execution_mode = :standalone
174
190
  end
175
191
  end
@@ -14,34 +14,6 @@ module Karafka
14
14
  class Config
15
15
  extend ::Karafka::Core::Configurable
16
16
 
17
- # Defaults for kafka settings, that will be overwritten only if not present already
18
- KAFKA_DEFAULTS = {
19
- # We emit the statistics by default, so all the instrumentation and web-ui work out of
20
- # the box, without requiring users to take any extra actions aside from enabling.
21
- 'statistics.interval.ms': 5_000,
22
- 'client.software.name': 'karafka',
23
- 'client.software.version': [
24
- "v#{Karafka::VERSION}",
25
- "rdkafka-ruby-v#{Rdkafka::VERSION}",
26
- "librdkafka-v#{Rdkafka::LIBRDKAFKA_VERSION}"
27
- ].join('-')
28
- }.freeze
29
-
30
- # Contains settings that should not be used in production but make life easier in dev
31
- KAFKA_DEV_DEFAULTS = {
32
- # Will create non-existing topics automatically.
33
- # Note that the broker needs to be configured with `auto.create.topics.enable=true`
34
- # While it is not recommended in prod, it simplifies work in dev
35
- 'allow.auto.create.topics': 'true',
36
- # We refresh the cluster state often as newly created topics in dev may not be detected
37
- # fast enough. Fast enough means within reasonable time to provide decent user experience
38
- # While it's only a one time thing for new topics, it can still be irritating to have to
39
- # restart the process.
40
- 'topic.metadata.refresh.interval.ms': 5_000
41
- }.freeze
42
-
43
- private_constant :KAFKA_DEFAULTS, :KAFKA_DEV_DEFAULTS
44
-
45
17
  # Available settings
46
18
 
47
19
  # Namespace for Pro version related license management. If you use LGPL, no need to worry
@@ -350,7 +322,6 @@ module Karafka
350
322
  Pro::Loader.pre_setup_all(config) if Karafka.pro?
351
323
 
352
324
  configure(&block)
353
- merge_kafka_defaults!(config)
354
325
 
355
326
  Contracts::Config.new.validate!(config.to_h)
356
327
 
@@ -374,26 +345,6 @@ module Karafka
374
345
 
375
346
  private
376
347
 
377
- # Propagates the kafka setting defaults unless they are already present
378
- # This makes it easier to set some values that users usually don't change but still allows
379
- # them to overwrite the whole hash if they want to
380
- # @param config [Karafka::Core::Configurable::Node] config of this producer
381
- def merge_kafka_defaults!(config)
382
- KAFKA_DEFAULTS.each do |key, value|
383
- next if config.kafka.key?(key)
384
-
385
- config.kafka[key] = value
386
- end
387
-
388
- return if Karafka::App.env.production?
389
-
390
- KAFKA_DEV_DEFAULTS.each do |key, value|
391
- next if config.kafka.key?(key)
392
-
393
- config.kafka[key] = value
394
- end
395
- end
396
-
397
348
  # Sets up all the components that are based on the user configuration
398
349
  # @note At the moment it is only WaterDrop
399
350
  def configure_components
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Setup
5
+ # Injector that enriches each Kafka cluster with needed defaults. User may use more than one
6
+ # cluster and define them on a per-topic basis. We use this when we build the final config
7
+ # per subscription group.
8
+ module DefaultsInjector
9
+ # Defaults for kafka settings, that will be overwritten only if not present already
10
+ CONSUMER_KAFKA_DEFAULTS = {
11
+ # We emit the statistics by default, so all the instrumentation and web-ui work out of
12
+ # the box, without requiring users to take any extra actions aside from enabling.
13
+ 'statistics.interval.ms': 5_000,
14
+ 'client.software.name': 'karafka',
15
+ # Same as librdkafka default, we inject it nonetheless to have it always available as
16
+ # some features may use this value for computation and it is better to ensure, we do
17
+ # always have it
18
+ 'max.poll.interval.ms': 300_000,
19
+ 'client.software.version': [
20
+ "v#{Karafka::VERSION}",
21
+ "rdkafka-ruby-v#{Rdkafka::VERSION}",
22
+ "librdkafka-v#{Rdkafka::LIBRDKAFKA_VERSION}"
23
+ ].join('-')
24
+ }.freeze
25
+
26
+ # Contains settings that should not be used in production but make life easier in dev
27
+ CONSUMER_KAFKA_DEV_DEFAULTS = {
28
+ # Will create non-existing topics automatically.
29
+ # Note that the broker needs to be configured with `auto.create.topics.enable=true`
30
+ # While it is not recommended in prod, it simplifies work in dev
31
+ 'allow.auto.create.topics': 'true',
32
+ # We refresh the cluster state often as newly created topics in dev may not be detected
33
+ # fast enough. Fast enough means within reasonable time to provide decent user experience
34
+ # While it's only a one time thing for new topics, it can still be irritating to have to
35
+ # restart the process.
36
+ 'topic.metadata.refresh.interval.ms': 5_000
37
+ }.freeze
38
+
39
+ private_constant :CONSUMER_KAFKA_DEFAULTS, :CONSUMER_KAFKA_DEV_DEFAULTS
40
+
41
+ class << self
42
+ # Propagates the kafka setting defaults unless they are already present for consumer config
43
+ # This makes it easier to set some values that users usually don't change but still allows
44
+ # them to overwrite the whole hash if they want to
45
+ # @param kafka_config [Hash] kafka scoped config
46
+ def consumer(kafka_config)
47
+ CONSUMER_KAFKA_DEFAULTS.each do |key, value|
48
+ next if kafka_config.key?(key)
49
+
50
+ kafka_config[key] = value
51
+ end
52
+
53
+ return if Karafka::App.env.production?
54
+
55
+ CONSUMER_KAFKA_DEV_DEFAULTS.each do |key, value|
56
+ next if kafka_config.key?(key)
57
+
58
+ kafka_config[key] = value
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -68,6 +68,8 @@ module Karafka
68
68
  monitor.subscribe(liveness_listener)
69
69
  monitor.instrument('swarm.node.after_fork', caller: self)
70
70
 
71
+ Karafka::Process.tags.add(:execution_mode, 'mode:swarm')
72
+ Server.execution_mode = :swarm
71
73
  Server.run
72
74
 
73
75
  @writer.close
@@ -3,5 +3,5 @@
3
3
  # Main module namespace
4
4
  module Karafka
5
5
  # Current Karafka version
6
- VERSION = '2.4.6'
6
+ VERSION = '2.4.7'
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.4.6
4
+ version: 2.4.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Mensfeld
@@ -35,7 +35,7 @@ cert_chain:
35
35
  AnG1dJU+yL2BK7vaVytLTstJME5mepSZ46qqIJXMuWob/YPDmVaBF39TDSG9e34s
36
36
  msG3BiCqgOgHAnL23+CN3Rt8MsuRfEtoTKpJVcCfoEoNHOkc
37
37
  -----END CERTIFICATE-----
38
- date: 2024-07-22 00:00:00.000000000 Z
38
+ date: 2024-08-01 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: base64
@@ -505,6 +505,7 @@ files:
505
505
  - lib/karafka/server.rb
506
506
  - lib/karafka/setup/attributes_map.rb
507
507
  - lib/karafka/setup/config.rb
508
+ - lib/karafka/setup/defaults_injector.rb
508
509
  - lib/karafka/setup/dsl.rb
509
510
  - lib/karafka/status.rb
510
511
  - lib/karafka/swarm.rb
@@ -524,7 +525,7 @@ files:
524
525
  - renovate.json
525
526
  homepage: https://karafka.io
526
527
  licenses:
527
- - LGPL-3.0
528
+ - LGPL-3.0-only
528
529
  - Commercial
529
530
  metadata:
530
531
  funding_uri: https://karafka.io/#become-pro
metadata.gz.sig CHANGED
Binary file