karafka 2.4.0 → 2.4.1

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: 8111bc347c891837ee1f7cbabd4d98ee1c55f2bb7366e72aa077628cc4052c14
4
- data.tar.gz: 9a616eb14fbbddc433e56691540613a728cab6163632981de1c9f21abee8f0b4
3
+ metadata.gz: 7f3d5fa334ede552c2b0097acf16a942318ef648bcdbdcb0e84ffefcd5e65772
4
+ data.tar.gz: d800e8e25a62df01000dee9e3ce5904f3ddf632f66ac49e0c71dde3a6702209b
5
5
  SHA512:
6
- metadata.gz: 35e52c0f5ffeace79cc58e6ef85918f6ff32c80233419dec83a5b99485cd6f5ac2b163f3d0a73e96b4ac424e27a81a3f8f848d3bed5e461b69eb28399f102d3c
7
- data.tar.gz: 544848837b53a5e0e64b9c842f8dadc63a486b784660e37a6f44a67947682c59cd112e6ac509e19de99b7b084402b38d7e00d7fff52a89c9e07172f593b3032e
6
+ metadata.gz: ac438f23ca449023ff9537a5aa007879f2420dfdfe1cd81c1715322cfea53af8112cdf77663547155a40ba4e154cbc8e9c81df23dff9a6f843bb0601395b7271
7
+ data.tar.gz: 3e267b623cee9b649182ac08d34a68780e696e9ba24fda448caecb762ebf726f436b1b5b3415240f8409b4c7b66fc62c39c319c58128e429580f3468db1e152e
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Karafka framework changelog
2
2
 
3
+ ## 2.4.1 (2024-05-10)
4
+ - [Enhancement] Allow for usage of producer variants and alternative producers with ActiveJob Jobs (Pro).
5
+ - [Enhancement] Support `:earliest` and `:latest` in `Karafka::Admin#seek_consumer_group`.
6
+ - [Enhancement] Align configuration attributes mapper with exact librdkafka version used and not master.
7
+ - [Maintenance] Use `base64` from RubyGems as it will no longer be part of standard library in Ruby 3.4.
8
+ - [Fix] Support migrating via aliases and plan with aliases usage.
9
+ - [Fix] Active with default set to `false` cannot be overwritten
10
+ - [Fix] Fix inheritance of ActiveJob adapter `karafka_options` partitioner and dispatch method.
11
+
3
12
  ## 2.4.0 (2024-04-26)
4
13
 
5
14
  This release contains **BREAKING** changes. Make sure to read and apply upgrade notes.
data/Gemfile CHANGED
@@ -11,8 +11,8 @@ gemspec
11
11
  # Since some of those are only needed for some specs, they should never be required automatically
12
12
  group :integrations do
13
13
  gem 'activejob', require: false
14
- gem 'karafka-testing', '>= 2.4.0.rc1', require: false
15
- gem 'karafka-web', '>= 0.9.0.rc3', require: false
14
+ gem 'karafka-testing', '>= 2.4.0', require: false
15
+ gem 'karafka-web', '>= 0.9.0', require: false
16
16
  gem 'rspec', require: false
17
17
  end
18
18
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka (2.4.0)
4
+ karafka (2.4.1)
5
+ base64 (~> 0.2)
5
6
  karafka-core (>= 2.4.0, < 2.5.0)
6
7
  waterdrop (>= 2.7.0, < 3.0.0)
7
8
  zeitwerk (~> 2.3)
@@ -23,7 +24,7 @@ GEM
23
24
  mutex_m
24
25
  tzinfo (~> 2.0)
25
26
  base64 (0.2.0)
26
- bigdecimal (3.1.7)
27
+ bigdecimal (3.1.8)
27
28
  byebug (11.1.3)
28
29
  concurrent-ruby (1.2.3)
29
30
  connection_pool (2.4.1)
@@ -36,27 +37,27 @@ GEM
36
37
  ffi (1.16.3)
37
38
  globalid (1.2.1)
38
39
  activesupport (>= 6.1)
39
- i18n (1.14.4)
40
+ i18n (1.14.5)
40
41
  concurrent-ruby (~> 1.0)
41
42
  karafka-core (2.4.0)
42
43
  karafka-rdkafka (>= 0.15.0, < 0.16.0)
43
- karafka-rdkafka (0.15.0)
44
+ karafka-rdkafka (0.15.1)
44
45
  ffi (~> 1.15)
45
46
  mini_portile2 (~> 2.6)
46
47
  rake (> 12)
47
- karafka-testing (2.4.0.rc2)
48
- karafka (>= 2.4.0.beta1, < 2.5.0)
49
- waterdrop (>= 2.7.0.rc1)
50
- karafka-web (0.9.0.rc3)
48
+ karafka-testing (2.4.3)
49
+ karafka (>= 2.4.0, < 2.5.0)
50
+ waterdrop (>= 2.7.0)
51
+ karafka-web (0.9.1)
51
52
  erubi (~> 1.4)
52
- karafka (>= 2.4.0.rc1, < 2.5.0)
53
- karafka-core (>= 2.4.0.rc2, < 2.5.0)
53
+ karafka (>= 2.4.0, < 2.5.0)
54
+ karafka-core (>= 2.4.0, < 2.5.0)
54
55
  roda (~> 3.68, >= 3.69)
55
56
  tilt (~> 2.0)
56
57
  mini_portile2 (2.8.6)
57
58
  minitest (5.22.3)
58
59
  mutex_m (0.2.0)
59
- rack (3.0.10)
60
+ rack (3.0.11)
60
61
  rake (13.2.1)
61
62
  roda (3.79.0)
62
63
  rack
@@ -69,7 +70,7 @@ GEM
69
70
  rspec-expectations (3.13.0)
70
71
  diff-lcs (>= 1.2.0, < 2.0)
71
72
  rspec-support (~> 3.13.0)
72
- rspec-mocks (3.13.0)
73
+ rspec-mocks (3.13.1)
73
74
  diff-lcs (>= 1.2.0, < 2.0)
74
75
  rspec-support (~> 3.13.0)
75
76
  rspec-support (3.13.1)
@@ -82,8 +83,9 @@ GEM
82
83
  tilt (2.3.0)
83
84
  tzinfo (2.0.6)
84
85
  concurrent-ruby (~> 1.0)
85
- waterdrop (2.7.0)
86
+ waterdrop (2.7.2)
86
87
  karafka-core (>= 2.4.0, < 3.0.0)
88
+ karafka-rdkafka (>= 0.15.1)
87
89
  zeitwerk (~> 2.3)
88
90
  zeitwerk (2.6.13)
89
91
 
@@ -96,8 +98,8 @@ DEPENDENCIES
96
98
  byebug
97
99
  factory_bot
98
100
  karafka!
99
- karafka-testing (>= 2.4.0.rc1)
100
- karafka-web (>= 0.9.0.rc3)
101
+ karafka-testing (>= 2.4.0)
102
+ karafka-web (>= 0.9.0)
101
103
  rspec
102
104
  simplecov
103
105
 
data/karafka.gemspec CHANGED
@@ -21,6 +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 'base64', '~> 0.2'
24
25
  spec.add_dependency 'karafka-core', '>= 2.4.0', '< 2.5.0'
25
26
  spec.add_dependency 'waterdrop', '>= 2.7.0', '< 3.0.0'
26
27
  spec.add_dependency 'zeitwerk', '~> 2.3'
@@ -23,6 +23,9 @@ module Karafka
23
23
  # them
24
24
  App.config.internal.active_job.job_options_contract.validate!(new_options)
25
25
 
26
+ # We need to modify this hash because otherwise we would modify parent hash.
27
+ self._karafka_options = _karafka_options.dup
28
+
26
29
  new_options.each do |name, value|
27
30
  _karafka_options[name] = value
28
31
  end
data/lib/karafka/admin.rb CHANGED
@@ -10,6 +10,12 @@ module Karafka
10
10
  # Cluster on which operations are performed can be changed via `admin.kafka` config, however
11
11
  # there is no multi-cluster runtime support.
12
12
  module Admin
13
+ # More or less number of seconds of 1 hundred years
14
+ # Used for time referencing that does not have to be accurate but needs to be big
15
+ HUNDRED_YEARS = 100 * 365.25 * 24 * 60 * 60
16
+
17
+ private_constant :HUNDRED_YEARS
18
+
13
19
  class << self
14
20
  # Allows us to read messages from the topic
15
21
  #
@@ -156,6 +162,18 @@ module Karafka
156
162
  #
157
163
  # @example Move offset to 5 seconds ago on partition 2
158
164
  # Karafka::Admin.seek_consumer_group('group-id', { 'topic' => { 2 => 5.seconds.ago } })
165
+ #
166
+ # @example Move to the earliest offset on all the partitions of a topic
167
+ # Karafka::Admin.seek_consumer_group('group-id', { 'topic' => 'earliest' })
168
+ #
169
+ # @example Move to the latest (high-watermark) offset on all the partitions of a topic
170
+ # Karafka::Admin.seek_consumer_group('group-id', { 'topic' => 'latest' })
171
+ #
172
+ # @example Move offset of a single partition to earliest
173
+ # Karafka::Admin.seek_consumer_group('group-id', { 'topic' => { 1 => 'earliest' } })
174
+ #
175
+ # @example Move offset of a single partition to latest
176
+ # Karafka::Admin.seek_consumer_group('group-id', { 'topic' => { 1 => 'latest' } })
159
177
  def seek_consumer_group(consumer_group_id, topics_with_partitions_and_offsets)
160
178
  tpl_base = {}
161
179
 
@@ -174,6 +192,32 @@ module Karafka
174
192
  end
175
193
  end
176
194
 
195
+ tpl_base.each_value do |partitions|
196
+ partitions.transform_values! do |position|
197
+ # Support both symbol and string based references
198
+ casted_position = position.is_a?(Symbol) ? position.to_s : position
199
+
200
+ # This remap allows us to transform some special cases in a reference that can be
201
+ # understood by Kafka
202
+ case casted_position
203
+ # Earliest is not always 0. When compacting/deleting it can be much later, that's why
204
+ # we fetch the oldest possible offset
205
+ when 'earliest'
206
+ Time.now - HUNDRED_YEARS
207
+ # Latest will always be the high-watermark offset and we can get it just by getting
208
+ # a future position
209
+ when 'latest'
210
+ Time.now + HUNDRED_YEARS
211
+ # Same as `'latest'`
212
+ when false
213
+ Time.now - HUNDRED_YEARS
214
+ # Regular offset case
215
+ else
216
+ position
217
+ end
218
+ end
219
+ end
220
+
177
221
  tpl = Rdkafka::Consumer::TopicPartitionList.new
178
222
  # In case of time based location, we need to to a pre-resolution, that's why we keep it
179
223
  # separately
@@ -89,9 +89,19 @@ module Karafka
89
89
  desired_configs.transform_keys!(&:to_s)
90
90
 
91
91
  topic_with_configs.configs.each do |config|
92
- next unless desired_configs.key?(config.name)
92
+ names = config.synonyms.map(&:name) << config.name
93
93
 
94
- desired_config = desired_configs.fetch(config.name)
94
+ # We move forward only if given topic config is for altering
95
+ next if (desired_configs.keys & names).empty?
96
+
97
+ desired_config = nil
98
+
99
+ # We then find last defined value in our configs for a given attribute
100
+ # Since attributes can have synonyms, we select last one, which will represent the
101
+ # last defined value in case someone defined same multiple times
102
+ desired_configs.each do |name, value|
103
+ desired_config = value if names.include?(name)
104
+ end
95
105
 
96
106
  # Do not migrate if existing and desired values are the same
97
107
  next if desired_config == config.value
@@ -143,14 +143,25 @@ module Karafka
143
143
  }
144
144
 
145
145
  scoped = @topics_to_alter[declarative][declarative_name]
146
+ # declarative name can be a synonym. In such cases we remap it during the discovery
147
+ # below
148
+ final_name = declarative_name
146
149
 
147
150
  topic_c.configs.each do |config|
148
- next unless declarative_name == config.name
151
+ names = config.synonyms.map(&:name) << config.name
149
152
 
153
+ next unless names.include?(declarative_name)
154
+
155
+ # Always use a non-synonym name if differs
156
+ final_name = config.name
150
157
  scoped[:action] = :change
151
158
  scoped[:from] = config.value
152
159
  end
153
160
 
161
+ # Aligns the name in case synonym was used
162
+ target = @topics_to_alter[declarative].delete(declarative_name)
163
+ @topics_to_alter[declarative][final_name] = target
164
+
154
165
  # Remove change definitions that would migrate to the same value as present
155
166
  @topics_to_alter[declarative].delete_if do |_name, details|
156
167
  details[:from] == details[:to]
@@ -28,14 +28,23 @@ module Karafka
28
28
  # each job.
29
29
  partitioner: nil,
30
30
  # Allows for usage of `:key` or `:partition_key`
31
- partition_key_type: :key
31
+ partition_key_type: :key,
32
+ # Allows for setting a callable producer since at the moment of defining the class,
33
+ # variants may not be available
34
+ #
35
+ # We do not initialize it with `-> { ::Karafka.producer }` so we do not have to call it
36
+ # each time for the defaults to preserve CPU cycles.
37
+ #
38
+ # We also do **not** cache the execution of this producer lambda because we want to
39
+ # support job args based producer selection
40
+ producer: nil
32
41
  }.freeze
33
42
 
34
43
  private_constant :DEFAULTS
35
44
 
36
45
  # @param job [ActiveJob::Base] job
37
46
  def dispatch(job)
38
- ::Karafka.producer.public_send(
47
+ producer(job).public_send(
39
48
  fetch_option(job, :dispatch_method, DEFAULTS),
40
49
  dispatch_details(job).merge!(
41
50
  topic: job.queue_name,
@@ -47,27 +56,46 @@ module Karafka
47
56
  # Bulk dispatches multiple jobs using the Rails 7.1+ API
48
57
  # @param jobs [Array<ActiveJob::Base>] jobs we want to dispatch
49
58
  def dispatch_many(jobs)
50
- dispatches = Hash.new { |hash, key| hash[key] = [] }
59
+ # First level is type of dispatch and second is the producer we want to use to dispatch
60
+ dispatches = Hash.new do |hash, key|
61
+ hash[key] = Hash.new do |hash2, key2|
62
+ hash2[key2] = []
63
+ end
64
+ end
51
65
 
52
66
  jobs.each do |job|
53
67
  d_method = fetch_option(job, :dispatch_many_method, DEFAULTS)
68
+ producer = producer(job)
54
69
 
55
- dispatches[d_method] << dispatch_details(job).merge!(
70
+ dispatches[d_method][producer] << dispatch_details(job).merge!(
56
71
  topic: job.queue_name,
57
72
  payload: ::ActiveSupport::JSON.encode(serialize_job(job))
58
73
  )
59
74
  end
60
75
 
61
- dispatches.each do |type, messages|
62
- ::Karafka.producer.public_send(
63
- type,
64
- messages
65
- )
76
+ dispatches.each do |d_method, producers|
77
+ producers.each do |producer, messages|
78
+ producer.public_send(
79
+ d_method,
80
+ messages
81
+ )
82
+ end
66
83
  end
67
84
  end
68
85
 
69
86
  private
70
87
 
88
+ # Selects the producer based on options. If callable `:producer` is defined, it will use
89
+ # it. If not, will just use the `Karafka.producer`.
90
+ #
91
+ # @param job [ActiveJob::Base] job instance
92
+ # @return [WaterDrop::Producer, WaterDrop::Producer::Variant] producer or a variant
93
+ def producer(job)
94
+ dynamic_producer = fetch_option(job, :producer, DEFAULTS)
95
+
96
+ dynamic_producer ? dynamic_producer.call(job) : ::Karafka.producer
97
+ end
98
+
71
99
  # @param job [ActiveJob::Base] job instance
72
100
  # @return [Hash] hash with dispatch details to which we merge topic and payload
73
101
  def dispatch_details(job)
@@ -81,7 +81,16 @@ module Karafka
81
81
  # Allows to disable topic by invoking this method and setting it to `false`.
82
82
  # @param active [Boolean] should this topic be consumed or not
83
83
  def active(active)
84
- @active = active
84
+ # Do not allow for active overrides. Basically if this is set on the topic level, defaults
85
+ # will not overwrite it and this is desired. Otherwise because of the fact that this is
86
+ # not a full feature config but just a flag, default value would always overwrite the
87
+ # per-topic config since defaults application happens after the topic config block
88
+ unless @active_assigned
89
+ @active = active
90
+ @active_assigned = true
91
+ end
92
+
93
+ @active
85
94
  end
86
95
 
87
96
  # @return [Class] consumer class that we should use
@@ -52,9 +52,7 @@ module Karafka
52
52
  fetch.wait.max.ms
53
53
  group.id
54
54
  group.instance.id
55
- group.protocol
56
55
  group.protocol.type
57
- group.remote.assignor
58
56
  heartbeat.interval.ms
59
57
  interceptors
60
58
  internal.termination.signal
@@ -153,6 +151,7 @@ module Karafka
153
151
  # List of rdkafka producer accepted attributes
154
152
  PRODUCER = %i[
155
153
  acks
154
+ allow.auto.create.topics
156
155
  api.version.fallback.ms
157
156
  api.version.request
158
157
  api.version.request.timeout.ms
@@ -289,7 +288,12 @@ module Karafka
289
288
  ].freeze
290
289
 
291
290
  # Location of the file with rdkafka settings list
292
- SOURCE = 'https://raw.githubusercontent.com/edenhill/librdkafka/master/CONFIGURATION.md'
291
+ SOURCE = <<~SOURCE.delete("\n").gsub(/\s+/, '/')
292
+ https://raw.githubusercontent.com
293
+ confluentinc/librdkafka
294
+ v#{Rdkafka::LIBRDKAFKA_VERSION}
295
+ CONFIGURATION.md
296
+ SOURCE
293
297
 
294
298
  private_constant :SOURCE
295
299
 
@@ -337,9 +341,6 @@ module Karafka
337
341
  end
338
342
  end
339
343
 
340
- # This can be removed when 0.13 librdkafka is released
341
- attributes[:producer].delete_if { |val| val == 'allow.auto.create.topics' }
342
-
343
344
  attributes.transform_values!(&:sort)
344
345
  attributes.each_value { |vals| vals.map!(&:to_sym) }
345
346
  attributes
@@ -3,5 +3,5 @@
3
3
  # Main module namespace
4
4
  module Karafka
5
5
  # Current Karafka version
6
- VERSION = '2.4.0'
6
+ VERSION = '2.4.1'
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.0
4
+ version: 2.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Mensfeld
@@ -35,8 +35,22 @@ cert_chain:
35
35
  AnG1dJU+yL2BK7vaVytLTstJME5mepSZ46qqIJXMuWob/YPDmVaBF39TDSG9e34s
36
36
  msG3BiCqgOgHAnL23+CN3Rt8MsuRfEtoTKpJVcCfoEoNHOkc
37
37
  -----END CERTIFICATE-----
38
- date: 2024-04-26 00:00:00.000000000 Z
38
+ date: 2024-05-10 00:00:00.000000000 Z
39
39
  dependencies:
40
+ - !ruby/object:Gem::Dependency
41
+ name: base64
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.2'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.2'
40
54
  - !ruby/object:Gem::Dependency
41
55
  name: karafka-core
42
56
  requirement: !ruby/object:Gem::Requirement
metadata.gz.sig CHANGED
Binary file