karafka 2.0.34 → 2.0.36

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: 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