karafka 2.0.34 → 2.0.36

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: 36d890d825aaeaee5349dcc653d888da3a023c01a837864544a905db977569c4
4
- data.tar.gz: be442485812a05a030bab33da31a8e2fda684add8c4d59a0af78f517bb2519bd
3
+ metadata.gz: 128d19fa065cbb43fae7a23a424e44ec30077fe58c93d7c122da299ec5b98df1
4
+ data.tar.gz: 0b396fa75d93257f32a9ddf94f0dc89d2456b6ed07acad5c581908d148cf0a6f
5
5
  SHA512:
6
- metadata.gz: d92be137485c436c1ed02435669785422e6e4da194ab19b97dca31f70b530fe7e0ae4e6b0c7c54895dbceca2d8fe4ef1bdcabdb287affe06e377942062777979
7
- data.tar.gz: 0525b652373088a7a692134a6a4c89487e495e01934504d256a60ae7805c92f65a308d38c8d75ad806f39f5128d2d00865e10b1b4ae326c7dd60e7594c68558e
6
+ metadata.gz: 68d4da94eaf7a1033c95dd02bdc9a4bd8e69040df4db304dbcb6f8255997a40c003c999eea6442f6c1a1640c3e1ed44032c3826191e96085afbccc8600a8f2b6
7
+ data.tar.gz: 025fbc8edb26f6f8442e11ea90afdc3e07873efcba3f6778549cd66e0738dfec73d956d877c44ea39bc1064c22df9fca6d3c68a42ef97ad4438ed7812b690ea1
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Karafka framework changelog
2
2
 
3
+ ## 2.0.36 (2023-03-17)
4
+ - [Refactor] Rename internal naming of `Structurable` to `Declaratives` for declarative topics feature.
5
+ - [Fix] AJ + DLQ + MOM + LRJ is pausing indefinitely after the first job (#1362)
6
+
7
+ ## 2.0.35 (2023-03-13)
8
+ - **[Feature]** Allow for defining topics config via the DSL and its automatic creation via CLI command.
9
+ - **[Feature]** Allow for full topics reset and topics repartitioning via the CLI.
10
+
3
11
  ## 2.0.34 (2023-03-04)
4
12
  - [Improvement] Attach an `embedded` tag to Karafka processes started using the embedded API.
5
13
  - [Change] Renamed `Datadog::Listener` to `Datadog::MetricsListener` for consistency. (#1124)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka (2.0.34)
4
+ karafka (2.0.36)
5
5
  karafka-core (>= 2.0.12, < 3.0.0)
6
6
  thor (>= 0.20)
7
7
  waterdrop (>= 2.4.10, < 3.0.0)
@@ -37,7 +37,7 @@ GEM
37
37
  mini_portile2 (~> 2.6)
38
38
  rake (> 12)
39
39
  mini_portile2 (2.8.1)
40
- minitest (5.17.0)
40
+ minitest (5.18.0)
41
41
  rake (13.0.6)
42
42
  rspec (3.12.0)
43
43
  rspec-core (~> 3.12.0)
@@ -61,12 +61,13 @@ GEM
61
61
  thor (1.2.1)
62
62
  tzinfo (2.0.6)
63
63
  concurrent-ruby (~> 1.0)
64
- waterdrop (2.4.11)
64
+ waterdrop (2.5.0)
65
65
  karafka-core (>= 2.0.12, < 3.0.0)
66
66
  zeitwerk (~> 2.3)
67
67
  zeitwerk (2.6.7)
68
68
 
69
69
  PLATFORMS
70
+ arm64-darwin-21
70
71
  x86_64-linux
71
72
 
72
73
  DEPENDENCIES
@@ -78,4 +79,4 @@ DEPENDENCIES
78
79
  simplecov
79
80
 
80
81
  BUNDLED WITH
81
- 2.4.6
82
+ 2.4.7
@@ -45,6 +45,10 @@ 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
+ declaratives.partitions_format: needs to be more or equal to 1
49
+ declaratives.active_format: needs to be true
50
+ declaratives.replication_factor_format: needs to be more or equal to 1
51
+ declaratives.details_format: needs to be a hash with only symbol keys
48
52
  inconsistent_namespacing: |
49
53
  needs to be consistent namespacing style
50
54
  disable this validation by setting config.strict_topics_namespacing to false
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ class Cli < Thor
5
+ # CLI actions related to Kafka cluster topics management
6
+ class Topics < Base
7
+ include Helpers::Colorize
8
+
9
+ desc 'Allows for the topics management (create, delete, reset, repartition)'
10
+ # @param action [String] action we want to take
11
+ def call(action = 'missing')
12
+ case action
13
+ when 'create'
14
+ create
15
+ when 'delete'
16
+ delete
17
+ when 'reset'
18
+ reset
19
+ when 'repartition'
20
+ repartition
21
+ when 'migrate'
22
+ migrate
23
+ else
24
+ raise ::ArgumentError, "Invalid topics action: #{action}"
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ # Creates topics based on the routing setup and configuration
31
+ def create
32
+ declaratives_routing_topics.each do |topic|
33
+ name = topic.name
34
+
35
+ if existing_topics_names.include?(name)
36
+ puts "#{yellow('Skipping')} because topic #{name} already exists."
37
+ else
38
+ puts "Creating topic #{name}..."
39
+ Admin.create_topic(
40
+ name,
41
+ topic.declaratives.partitions,
42
+ topic.declaratives.replication_factor,
43
+ topic.declaratives.details
44
+ )
45
+ puts "#{green('Created')} topic #{name}."
46
+ end
47
+ end
48
+ end
49
+
50
+ # Deletes routing based topics
51
+ def delete
52
+ declaratives_routing_topics.each do |topic|
53
+ name = topic.name
54
+
55
+ if existing_topics_names.include?(name)
56
+ puts "Deleting topic #{name}..."
57
+ Admin.delete_topic(name)
58
+ puts "#{green('Deleted')} topic #{name}."
59
+ else
60
+ puts "#{yellow('Skipping')} because topic #{name} does not exist."
61
+ end
62
+ end
63
+ end
64
+
65
+ # Deletes routing based topics and re-creates them
66
+ def reset
67
+ delete
68
+
69
+ # We need to invalidate the metadata cache, otherwise we will think, that the topic
70
+ # already exists
71
+ @existing_topics = nil
72
+
73
+ create
74
+ end
75
+
76
+ # Creates missing topics and aligns the partitions count
77
+ def migrate
78
+ create
79
+
80
+ @existing_topics = nil
81
+
82
+ repartition
83
+ end
84
+
85
+ # Increases number of partitions on topics that have less partitions than defined
86
+ # Will **not** create topics if missing.
87
+ def repartition
88
+ existing_partitions = existing_topics.map do |topic|
89
+ [topic.fetch(:topic_name), topic.fetch(:partition_count)]
90
+ end.to_h
91
+
92
+ declaratives_routing_topics.each do |topic|
93
+ name = topic.name
94
+
95
+ desired_count = topic.config.partitions
96
+ existing_count = existing_partitions.fetch(name, false)
97
+
98
+ if existing_count && existing_count < desired_count
99
+ puts "Increasing number of partitions to #{desired_count} on topic #{name}..."
100
+ Admin.create_partitions(name, desired_count)
101
+ change = desired_count - existing_count
102
+ puts "#{green('Created')} #{change} additional partitions on topic #{name}."
103
+ elsif existing_count
104
+ puts "#{yellow('Skipping')} because topic #{name} has #{existing_count} partitions."
105
+ else
106
+ puts "#{yellow('Skipping')} because topic #{name} does not exist."
107
+ end
108
+ end
109
+ end
110
+
111
+ # @return [Array<Karafka::Routing::Topic>] all available topics that can be managed
112
+ # @note If topic is defined in multiple consumer groups, first config will be used. This
113
+ # means, that this CLI will not work for simultaneous management of multiple clusters from
114
+ # a single CLI command execution flow.
115
+ def declaratives_routing_topics
116
+ return @declaratives_routing_topics if @declaratives_routing_topics
117
+
118
+ collected_topics = {}
119
+
120
+ App.consumer_groups.each do |consumer_group|
121
+ consumer_group.topics.each do |topic|
122
+ # Skip topics that were explicitly disabled from management
123
+ next unless topic.declaratives.active?
124
+
125
+ collected_topics[topic.name] ||= topic
126
+ end
127
+ end
128
+
129
+ @declaratives_routing_topics = collected_topics.values
130
+ end
131
+
132
+ # @return [Array<Hash>] existing topics details
133
+ def existing_topics
134
+ @existing_topics ||= Admin.cluster_info.topics
135
+ end
136
+
137
+ # @return [Array<String>] names of already existing topics
138
+ def existing_topics_names
139
+ existing_topics.map { |topic| topic.fetch(:topic_name) }
140
+ end
141
+ end
142
+ end
143
+ end
@@ -15,6 +15,12 @@ module Karafka
15
15
  def red(string)
16
16
  "\033[0;31m#{string}\033[0m"
17
17
  end
18
+
19
+ # @param string [String] string we want to have in yellow
20
+ # @return [String] yellow string
21
+ def yellow(string)
22
+ "\033[1;33m#{string}\033[0m"
23
+ end
18
24
  end
19
25
  end
20
26
  end
@@ -23,8 +23,10 @@ module Karafka
23
23
  # This case is a bit of special. Please see the `AjDlqMom` for explanation on how the
24
24
  # offset management works in this case.
25
25
  module AjDlqLrjMom
26
- include AjLrjMom
27
- include AjDlqMom
26
+ # We can use the same code as for VP because non VP behaves like:
27
+ # - with one virtual partition
28
+ # - with "never ending" collapse
29
+ include AjDlqLrjMomVp
28
30
 
29
31
  # Features for this strategy
30
32
  FEATURES = %i[
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Routing
5
+ module Features
6
+ class Declaratives < Base
7
+ # Config for declarative topics feature
8
+ Config = Struct.new(
9
+ :active,
10
+ :partitions,
11
+ :replication_factor,
12
+ :details,
13
+ keyword_init: true
14
+ ) { alias_method :active?, :active }
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Routing
5
+ module Features
6
+ class Declaratives < Base
7
+ # Basic validation of the Kafka expected config details
8
+ class Contract < Contracts::Base
9
+ configure do |config|
10
+ config.error_messages = YAML.safe_load(
11
+ File.read(
12
+ File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
13
+ )
14
+ ).fetch('en').fetch('validations').fetch('topic')
15
+ end
16
+
17
+ nested :declaratives do
18
+ required(:active) { |val| [true, false].include?(val) }
19
+ required(:partitions) { |val| val.is_a?(Integer) && val.positive? }
20
+ required(:replication_factor) { |val| val.is_a?(Integer) && val.positive? }
21
+ required(:details) do |val|
22
+ val.is_a?(Hash) &&
23
+ val.keys.all? { |key| key.is_a?(Symbol) }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Routing
5
+ module Features
6
+ class Declaratives < Base
7
+ # Extension for managing Kafka topic configuration
8
+ module Topic
9
+ # @param active [Boolean] is the topic structure management feature active
10
+ # @param partitions [Integer]
11
+ # @param replication_factor [Integer]
12
+ # @param details [Hash] extra configuration for the topic
13
+ # @return [Config] defined structure
14
+ def config(active: true, partitions: 1, replication_factor: 1, **details)
15
+ @declaratives ||= Config.new(
16
+ active: active,
17
+ partitions: partitions,
18
+ replication_factor: replication_factor,
19
+ details: details
20
+ )
21
+ end
22
+
23
+ # @return [Config] config details
24
+ def declaratives
25
+ config
26
+ end
27
+
28
+ # @return [true] declaratives is always active
29
+ def declaratives?
30
+ declaratives.active?
31
+ end
32
+
33
+ # @return [Hash] topic with all its native configuration options plus declaratives
34
+ # settings
35
+ def to_h
36
+ super.merge(
37
+ declaratives: declaratives.to_h
38
+ ).freeze
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Routing
5
+ module Features
6
+ # This feature allows to store per topic structure that can be later on used to bootstrap
7
+ # topics structure for test/development, etc. This allows to share the same set of settings
8
+ # for topics despite the environment. Pretty much similar to how the `structure.sql` or
9
+ # `schema.rb` operate for SQL dbs.
10
+ class Declaratives < Base
11
+ end
12
+ end
13
+ end
14
+ end
@@ -53,6 +53,11 @@ class KarafkaApp < Karafka::App
53
53
  # active_job_topic :default
54
54
  <% end -%>
55
55
  topic :example do
56
+ # Uncomment this if you want Karafka to manage your topics configuration
57
+ # Managing topics configuration via routing will allow you to ensure config consistency
58
+ # across multiple environments
59
+ #
60
+ # config(partitions: 2, 'cleanup.policy': 'compact')
56
61
  consumer ExampleConsumer
57
62
  end
58
63
  end
@@ -3,5 +3,5 @@
3
3
  # Main module namespace
4
4
  module Karafka
5
5
  # Current Karafka version
6
- VERSION = '2.0.34'
6
+ VERSION = '2.0.36'
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.34
4
+ version: 2.0.36
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-03-04 00:00:00.000000000 Z
38
+ date: 2023-03-17 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: karafka-core
@@ -168,6 +168,7 @@ files:
168
168
  - lib/karafka/cli/info.rb
169
169
  - lib/karafka/cli/install.rb
170
170
  - lib/karafka/cli/server.rb
171
+ - lib/karafka/cli/topics.rb
171
172
  - lib/karafka/connection/client.rb
172
173
  - lib/karafka/connection/consumer_group_coordinator.rb
173
174
  - lib/karafka/connection/listener.rb
@@ -305,6 +306,10 @@ files:
305
306
  - lib/karafka/routing/features/dead_letter_queue/config.rb
306
307
  - lib/karafka/routing/features/dead_letter_queue/contract.rb
307
308
  - lib/karafka/routing/features/dead_letter_queue/topic.rb
309
+ - lib/karafka/routing/features/declaratives.rb
310
+ - lib/karafka/routing/features/declaratives/config.rb
311
+ - lib/karafka/routing/features/declaratives/contract.rb
312
+ - lib/karafka/routing/features/declaratives/topic.rb
308
313
  - lib/karafka/routing/features/manual_offset_management.rb
309
314
  - lib/karafka/routing/features/manual_offset_management/config.rb
310
315
  - lib/karafka/routing/features/manual_offset_management/contract.rb
metadata.gz.sig CHANGED
Binary file