karafka 2.0.30 → 2.0.31

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2c983b3fbf07db3e91f3c051a8b4de4b2d88619877fbf66a4e434e38bf67c96b
4
- data.tar.gz: babb3c513e6bb3fd0610363e3bbd9e7fcf07b1820f02fdd80ccacc8a109aab08
3
+ metadata.gz: a4c598dcc6414c2a24f50452d5379c2a0aa26d7d1c7fd8d1b07f2af19a6e8125
4
+ data.tar.gz: 184a8b1e7bb1f62672f90904ce6c3fd5e2da5f1868bcb2a971a1e7872e8c3252
5
5
  SHA512:
6
- metadata.gz: ea000c13a834ffba97c1837fa1f19dabbf78dbe8958354a8200bb5d2c0940852343521e6740113e07890f97a7e6edd108d8595a66688c4c14dbf313e86ec715f
7
- data.tar.gz: 81ae980ef7950c6cae7d1bea2751d1826e5bbf4c1384be4736af895977fb7ff38f70c24a9b7b248cb18744e042ffb0ba0dc154171c29dcc73262733d87c31ea1
6
+ metadata.gz: a9d382efa4846f4419d86d1fa2742f663e2c255b203612b6810ab5bc44aafc8c9a1c6c299330889c0cc54cb952235ddbb8d45a3c2ed30c7132b8522fc46ef8d1
7
+ data.tar.gz: 5a5907e0e7b958a6277785ef4254ac5d8cf7f563e371fdf3fe7705719164362858b46efae62dda6c7a1f02646ffd57d05437fde496ebf2b7e36bdcb8ac57865d
checksums.yaml.gz.sig CHANGED
Binary file
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.2.0
1
+ 3.2.1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Karafka framework changelog
2
2
 
3
+ ## 2.0.31 (2022-02-12)
4
+ - [Feature] Allow for adding partitions via `Admin#create_partitions` API.
5
+ - [Fix] Do not ignore admin errors upon invalid configuration (#1254)
6
+ - [Fix] Topic name validation (#1300) - CandyFet
7
+ - [Improvement] Increase the `max_wait_timeout` on admin operations to five minutes to make sure no timeout on heavily loaded clusters.
8
+ - [Maintenance] Require `karafka-core` >= `2.0.11` and switch to shared RSpec locator.
9
+ - [Maintenance] Require `karafka-rdkafka` >= `0.12.1`
10
+
3
11
  ## 2.0.30 (2022-01-31)
4
12
  - [Improvement] Alias `--consumer-groups` with `--include-consumer-groups`
5
13
  - [Improvement] Alias `--subscription-groups` with `--include-subscription-groups`
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka (2.0.30)
5
- karafka-core (>= 2.0.9, < 3.0.0)
4
+ karafka (2.0.31)
5
+ karafka-core (>= 2.0.11, < 3.0.0)
6
6
  thor (>= 0.20)
7
7
  waterdrop (>= 2.4.10, < 3.0.0)
8
8
  zeitwerk (~> 2.3)
@@ -29,10 +29,10 @@ GEM
29
29
  activesupport (>= 5.0)
30
30
  i18n (1.12.0)
31
31
  concurrent-ruby (~> 1.0)
32
- karafka-core (2.0.9)
32
+ karafka-core (2.0.11)
33
33
  concurrent-ruby (>= 1.1)
34
- karafka-rdkafka (>= 0.12)
35
- karafka-rdkafka (0.12.0)
34
+ karafka-rdkafka (>= 0.12.1)
35
+ karafka-rdkafka (0.12.1)
36
36
  ffi (~> 1.15)
37
37
  mini_portile2 (~> 2.6)
38
38
  rake (> 12)
@@ -43,7 +43,7 @@ GEM
43
43
  rspec-core (~> 3.12.0)
44
44
  rspec-expectations (~> 3.12.0)
45
45
  rspec-mocks (~> 3.12.0)
46
- rspec-core (3.12.0)
46
+ rspec-core (3.12.1)
47
47
  rspec-support (~> 3.12.0)
48
48
  rspec-expectations (3.12.2)
49
49
  diff-lcs (>= 1.2.0, < 2.0)
@@ -79,4 +79,4 @@ DEPENDENCIES
79
79
  simplecov
80
80
 
81
81
  BUNDLED WITH
82
- 2.4.5
82
+ 2.4.6
@@ -45,10 +45,12 @@ en:
45
45
  dead_letter_queue.topic_format: 'needs to be a string with a Kafka accepted format'
46
46
  dead_letter_queue.active_format: needs to be either true or false
47
47
  active_format: needs to be either true or false
48
+ inconsistent_namespacing: needs to be consistent namespacing style
48
49
 
49
50
  consumer_group:
50
51
  missing: needs to be present
51
52
  topics_names_not_unique: all topic names within a single consumer group must be unique
53
+ topics_namespaced_names_not_unique: all topic names within a single consumer group must be unique considering namespacing styles
52
54
  id_format: 'needs to be a string with a Kafka accepted format'
53
55
  topics_format: needs to be a non-empty array
54
56
 
data/karafka.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
  without having to focus on things that are not your business domain.
22
22
  DESC
23
23
 
24
- spec.add_dependency 'karafka-core', '>= 2.0.9', '< 3.0.0'
24
+ spec.add_dependency 'karafka-core', '>= 2.0.11', '< 3.0.0'
25
25
  spec.add_dependency 'thor', '>= 0.20'
26
26
  spec.add_dependency 'waterdrop', '>= 2.4.10', '< 3.0.0'
27
27
  spec.add_dependency 'zeitwerk', '~> 2.3'
data/lib/karafka/admin.rb CHANGED
@@ -14,6 +14,13 @@ module Karafka
14
14
  # do not have in the routing
15
15
  Topic = Struct.new(:name, :deserializer)
16
16
 
17
+ # We wait only for this amount of time before raising error as we intercept this error and
18
+ # retry after checking that the operation was finished or failed using external factor.
19
+ MAX_WAIT_TIMEOUT = 1
20
+
21
+ # How many times should be try. 1 x 60 => 60 seconds wait in total
22
+ MAX_ATTEMPTS = 60
23
+
17
24
  # Defaults for config
18
25
  CONFIG_DEFAULTS = {
19
26
  'group.id': 'karafka_admin',
@@ -22,7 +29,7 @@ module Karafka
22
29
  'statistics.interval.ms': 0
23
30
  }.freeze
24
31
 
25
- private_constant :Topic, :CONFIG_DEFAULTS
32
+ private_constant :Topic, :CONFIG_DEFAULTS, :MAX_WAIT_TIMEOUT, :MAX_ATTEMPTS
26
33
 
27
34
  class << self
28
35
  # Allows us to read messages from the topic
@@ -86,9 +93,12 @@ module Karafka
86
93
  # https://kafka.apache.org/documentation/#topicconfigs
87
94
  def create_topic(name, partitions, replication_factor, topic_config = {})
88
95
  with_admin do |admin|
89
- admin.create_topic(name, partitions, replication_factor, topic_config)
96
+ handler = admin.create_topic(name, partitions, replication_factor, topic_config)
90
97
 
91
- sleep(0.2) until topics_names.include?(name)
98
+ with_re_wait(
99
+ -> { handler.wait(max_wait_timeout: MAX_WAIT_TIMEOUT) },
100
+ -> { topics_names.include?(name) }
101
+ )
92
102
  end
93
103
  end
94
104
 
@@ -97,9 +107,27 @@ module Karafka
97
107
  # @param name [String] topic name
98
108
  def delete_topic(name)
99
109
  with_admin do |admin|
100
- admin.delete_topic(name)
110
+ handler = admin.delete_topic(name)
101
111
 
102
- sleep(0.2) while topics_names.include?(name)
112
+ with_re_wait(
113
+ -> { handler.wait(max_wait_timeout: MAX_WAIT_TIMEOUT) },
114
+ -> { !topics_names.include?(name) }
115
+ )
116
+ end
117
+ end
118
+
119
+ # Creates more partitions for a given topic
120
+ #
121
+ # @param name [String] topic name
122
+ # @param partitions [Integer] total number of partitions we expect to end up with
123
+ def create_partitions(name, partitions)
124
+ with_admin do |admin|
125
+ handler = admin.create_partitions(name, partitions)
126
+
127
+ with_re_wait(
128
+ -> { handler.wait(max_wait_timeout: MAX_WAIT_TIMEOUT) },
129
+ -> { topic(name).fetch(:partition_count) >= partitions }
130
+ )
103
131
  end
104
132
  end
105
133
 
@@ -117,6 +145,13 @@ module Karafka
117
145
  cluster_info.topics.map { |topic| topic.fetch(:topic_name) }
118
146
  end
119
147
 
148
+ # Finds details about given topic
149
+ # @param name [String] topic name
150
+ # @return [Hash] topic details
151
+ def topic(name)
152
+ cluster_info.topics.find { |topic| topic[:topic_name] == name }
153
+ end
154
+
120
155
  # Creates admin instance and yields it. After usage it closes the admin instance
121
156
  def with_admin
122
157
  admin = config(:producer).admin
@@ -133,6 +168,28 @@ module Karafka
133
168
  consumer&.close
134
169
  end
135
170
 
171
+ # There are some cases where rdkafka admin operations finish successfully but without the
172
+ # callback being triggered to materialize the post-promise object. Until this is fixed we
173
+ # can figure out, that operation we wanted to do finished successfully by checking that the
174
+ # effect of the command (new topic, more partitions, etc) is handled. Exactly for that we
175
+ # use the breaker. It we get a timeout, we can check that what we wanted to achieve has
176
+ # happened via the breaker check, hence we do not need to wait any longer.
177
+ #
178
+ # @param handler [Proc] the wait handler operation
179
+ # @param breaker [Proc] extra condition upon timeout that indicates things were finished ok
180
+ def with_re_wait(handler, breaker)
181
+ attempt ||= 0
182
+ attempt += 1
183
+
184
+ handler.call
185
+ rescue Rdkafka::AbstractHandle::WaitTimeoutError
186
+ return if breaker.call
187
+
188
+ retry if attempt <= MAX_ATTEMPTS
189
+
190
+ raise
191
+ end
192
+
136
193
  # @param type [Symbol] type of config we want
137
194
  # @return [::Rdkafka::Config] rdkafka config
138
195
  def config(type)
@@ -24,6 +24,32 @@ module Karafka
24
24
 
25
25
  [[%i[topics], :names_not_unique]]
26
26
  end
27
+
28
+ virtual do |data, errors|
29
+ next unless errors.empty?
30
+
31
+ names = data.fetch(:topics).map { |topic| topic[:name] }
32
+ names_hash = names.each_with_object({}) { |n, h| h[n] = true }
33
+ error_occured = false
34
+ names.each do |n|
35
+ # Skip topic names that are not namespaced
36
+ next unless n.chars.find { |c| ['.', '_'].include?(c) }
37
+
38
+ if n.chars.include?('.')
39
+ # Check underscore styled topic
40
+ underscored_topic = n.tr('.', '_')
41
+ error_occured = names_hash[underscored_topic] ? true : false
42
+ else
43
+ # Check dot styled topic
44
+ dot_topic = n.tr('_', '.')
45
+ error_occured = names_hash[dot_topic] ? true : false
46
+ end
47
+ end
48
+
49
+ next unless error_occured
50
+
51
+ [[%i[topics], :topics_namespaced_names_not_unique]]
52
+ end
27
53
  end
28
54
  end
29
55
  end
@@ -48,6 +48,17 @@ module Karafka
48
48
  [[%w[kafka], e.message]]
49
49
  end
50
50
  end
51
+
52
+ virtual do |data, errors|
53
+ next unless errors.empty?
54
+
55
+ value = data.fetch(:name)
56
+ namespacing_chars_count = value.chars.find_all { |c| ['.', '_'].include?(c) }.uniq.count
57
+
58
+ next if namespacing_chars_count <= 1
59
+
60
+ [[%w[name], :inconsistent_namespacing]]
61
+ end
51
62
  end
52
63
  end
53
64
  end
@@ -5,6 +5,6 @@ module Karafka
5
5
  module Contracts
6
6
  # Regexp for validating format of groups and topics
7
7
  # @note It is not nested inside of the contracts, as it is used by couple of them
8
- TOPIC_REGEXP = /\A(\w|-|\.)+\z/
8
+ TOPIC_REGEXP = /^[A-Za-z0-9\-_.]+$/
9
9
  end
10
10
  end
@@ -3,5 +3,5 @@
3
3
  # Main module namespace
4
4
  module Karafka
5
5
  # Current Karafka version
6
- VERSION = '2.0.30'
6
+ VERSION = '2.0.31'
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.30
4
+ version: 2.0.31
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Mensfeld
@@ -35,7 +35,7 @@ cert_chain:
35
35
  Qf04B9ceLUaC4fPVEz10FyobjaFoY4i32xRto3XnrzeAgfEe4swLq8bQsR3w/EF3
36
36
  MGU0FeSV2Yj7Xc2x/7BzLK8xQn5l7Yy75iPF+KP3vVmDHnNl
37
37
  -----END CERTIFICATE-----
38
- date: 2023-01-31 00:00:00.000000000 Z
38
+ date: 2023-02-13 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: karafka-core
@@ -43,7 +43,7 @@ dependencies:
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: 2.0.9
46
+ version: 2.0.11
47
47
  - - "<"
48
48
  - !ruby/object:Gem::Version
49
49
  version: 3.0.0
@@ -53,7 +53,7 @@ dependencies:
53
53
  requirements:
54
54
  - - ">="
55
55
  - !ruby/object:Gem::Version
56
- version: 2.0.9
56
+ version: 2.0.11
57
57
  - - "<"
58
58
  - !ruby/object:Gem::Version
59
59
  version: 3.0.0
@@ -356,7 +356,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
356
356
  - !ruby/object:Gem::Version
357
357
  version: '0'
358
358
  requirements: []
359
- rubygems_version: 3.4.1
359
+ rubygems_version: 3.4.6
360
360
  signing_key:
361
361
  specification_version: 4
362
362
  summary: Karafka is Ruby and Rails efficient Kafka processing framework.
metadata.gz.sig CHANGED
Binary file