waterdrop 2.6.11 → 2.6.12

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: 3798d61ff3464dd6aaeb21f5457230a3b079748f260c59138142144efc62dc40
4
- data.tar.gz: c9699bc202a67fbb8e456a4e0d316f56e8635e0986a09d0429a25999553c4a76
3
+ metadata.gz: b69e4151ceb0d03f8ac34e05793f96e4907791c5dbdc49f5beeffa7eb02d5bcf
4
+ data.tar.gz: fef1a647d06d7f7ab3f26045f1ce22e7b2b40476c31b7526198291a55d73c828
5
5
  SHA512:
6
- metadata.gz: c6a6d03d205314775b71ca0c4e922ef52159965e30b9280727712f25368f6af78c821d6d468b2362bd4094fbd2d4747195dc1e3a7cd76a0e4582f81a66829b43
7
- data.tar.gz: 9541ae05afde65cf1124661fd57e23afb4265601748c6b4a60f6fc6f8cabe463cf59b6ac683e7d047e454cc671182808f800add0f68b780bbb1b1325abcab892
6
+ metadata.gz: 271f3de27ae484cb95b6dbc0718bbe59119262035f130511d7ef84071b598dd3e06695f233ad9335c7e318e5da754b3ec2852b1655c23d28ee2d88d67aff901c
7
+ data.tar.gz: eabff4ab9ea4c832054583d52dd83d003d27333394d8630133b08d3f46d9c11278c6bd76dd4408750f4d0c41654eb2d70d94f83fbedf2250dfa0b2538a6e8e30
checksums.yaml.gz.sig CHANGED
Binary file
@@ -1,6 +1,8 @@
1
1
  name: ci
2
2
 
3
- concurrency: ci-${{ github.ref }}
3
+ concurrency:
4
+ group: ${{ github.workflow }}-${{ github.ref }}
5
+ cancel-in-progress: true
4
6
 
5
7
  on:
6
8
  pull_request:
@@ -16,13 +18,13 @@ jobs:
16
18
  fail-fast: false
17
19
  matrix:
18
20
  ruby:
19
- - '3.3.0-preview2'
21
+ - '3.3'
20
22
  - '3.2'
21
23
  - '3.1'
22
24
  - '3.0'
23
25
  - '2.7'
24
26
  include:
25
- - ruby: '3.2'
27
+ - ruby: '3.3'
26
28
  coverage: 'true'
27
29
  steps:
28
30
  - uses: actions/checkout@v4
@@ -35,6 +37,7 @@ jobs:
35
37
  with:
36
38
  ruby-version: ${{matrix.ruby}}
37
39
  bundler-cache: true
40
+ bundler: 'latest'
38
41
 
39
42
  - name: Run Kafka with docker-compose
40
43
  run: |
@@ -46,13 +49,25 @@ jobs:
46
49
 
47
50
  - name: Install latest bundler
48
51
  run: |
49
- gem install bundler --no-document
52
+ if [[ "$(ruby -v | awk '{print $2}')" == 2.7.8* ]]; then
53
+ gem install bundler -v 2.4.22 --no-document
54
+ gem update --system 3.4.22 --no-document
55
+ else
56
+ gem install bundler --no-document
57
+ gem update --system --no-document
58
+ fi
59
+
50
60
  bundle config set without 'tools benchmarks docs'
51
61
 
52
62
  - name: Bundle install
53
63
  run: |
54
64
  bundle config set without development
55
- bundle install --jobs 4 --retry 3
65
+
66
+ if [[ "$(ruby -v | awk '{print $2}')" == 2.7.8* ]]; then
67
+ BUNDLER_VERSION=2.4.22 bundle install --jobs 4 --retry 3
68
+ else
69
+ bundle install --jobs 4 --retry 3
70
+ fi
56
71
 
57
72
  - name: Run all tests
58
73
  env:
@@ -70,7 +85,7 @@ jobs:
70
85
  - name: Set up Ruby
71
86
  uses: ruby/setup-ruby@v1
72
87
  with:
73
- ruby-version: 3.2
88
+ ruby-version: 3.3
74
89
  - name: Install latest bundler
75
90
  run: gem install bundler --no-document
76
91
  - name: Install Diffend plugin
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.2.2
1
+ 3.3.0
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # WaterDrop changelog
2
2
 
3
+ ## 2.6.12 (2024-01-03)
4
+ - [Enhancement] Provide ability to label message dispatches for increased observability.
5
+ - [Enhancement] Provide ability to commit offset during the transaction with a consumer provided.
6
+ - [Change] Change transactional message purged error type from `message.error` to `librdkafka.dispatch_error` to align with the non-transactional error type.
7
+ - [Change] Remove usage of concurrent ruby.
8
+
3
9
  ## 2.6.11 (2023-10-25)
4
10
  - [Enhancement] Return delivery handles and delivery report for both dummy and buffered clients with proper topics, partitions and offsets assign and auto-increment offsets per partition.
5
11
  - [Fix] Fix a case where buffered test client would not accumulate messages on failed transactions
data/Gemfile.lock CHANGED
@@ -1,37 +1,48 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- waterdrop (2.6.11)
4
+ waterdrop (2.6.12)
5
5
  karafka-core (>= 2.2.3, < 3.0.0)
6
6
  zeitwerk (~> 2.3)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- activesupport (7.0.7.2)
11
+ activesupport (7.1.2)
12
+ base64
13
+ bigdecimal
12
14
  concurrent-ruby (~> 1.0, >= 1.0.2)
15
+ connection_pool (>= 2.2.5)
16
+ drb
13
17
  i18n (>= 1.6, < 2)
14
18
  minitest (>= 5.1)
19
+ mutex_m
15
20
  tzinfo (~> 2.0)
21
+ base64 (0.2.0)
22
+ bigdecimal (3.1.5)
16
23
  byebug (11.1.3)
17
24
  concurrent-ruby (1.2.2)
25
+ connection_pool (2.4.1)
18
26
  diff-lcs (1.5.0)
19
27
  docile (1.4.0)
20
- factory_bot (6.3.0)
28
+ drb (2.2.0)
29
+ ruby2_keywords
30
+ factory_bot (6.4.5)
21
31
  activesupport (>= 5.0.0)
22
- ffi (1.15.5)
32
+ ffi (1.16.3)
23
33
  i18n (1.14.1)
24
34
  concurrent-ruby (~> 1.0)
25
- karafka-core (2.2.3)
35
+ karafka-core (2.2.7)
26
36
  concurrent-ruby (>= 1.1)
27
- karafka-rdkafka (>= 0.13.6, < 0.14.0)
28
- karafka-rdkafka (0.13.6)
37
+ karafka-rdkafka (>= 0.13.9, < 0.15.0)
38
+ karafka-rdkafka (0.14.6)
29
39
  ffi (~> 1.15)
30
40
  mini_portile2 (~> 2.6)
31
41
  rake (> 12)
32
- mini_portile2 (2.8.2)
33
- minitest (5.19.0)
34
- rake (13.0.6)
42
+ mini_portile2 (2.8.5)
43
+ minitest (5.20.0)
44
+ mutex_m (0.2.0)
45
+ rake (13.1.0)
35
46
  rspec (3.12.0)
36
47
  rspec-core (~> 3.12.0)
37
48
  rspec-expectations (~> 3.12.0)
@@ -41,10 +52,11 @@ GEM
41
52
  rspec-expectations (3.12.3)
42
53
  diff-lcs (>= 1.2.0, < 2.0)
43
54
  rspec-support (~> 3.12.0)
44
- rspec-mocks (3.12.5)
55
+ rspec-mocks (3.12.6)
45
56
  diff-lcs (>= 1.2.0, < 2.0)
46
57
  rspec-support (~> 3.12.0)
47
- rspec-support (3.12.0)
58
+ rspec-support (3.12.1)
59
+ ruby2_keywords (0.0.5)
48
60
  simplecov (0.22.0)
49
61
  docile (~> 1.1)
50
62
  simplecov-html (~> 0.11)
@@ -53,9 +65,10 @@ GEM
53
65
  simplecov_json_formatter (0.1.4)
54
66
  tzinfo (2.0.6)
55
67
  concurrent-ruby (~> 1.0)
56
- zeitwerk (2.6.8)
68
+ zeitwerk (2.6.12)
57
69
 
58
70
  PLATFORMS
71
+ ruby
59
72
  x86_64-linux
60
73
 
61
74
  DEPENDENCIES
@@ -66,4 +79,4 @@ DEPENDENCIES
66
79
  waterdrop!
67
80
 
68
81
  BUNDLED WITH
69
- 2.4.12
82
+ 2.5.3
@@ -27,6 +27,12 @@ en:
27
27
  headers_invalid_key_type: all headers keys need to be of type String
28
28
  headers_invalid_value_type: all headers values need to be of type String
29
29
 
30
+ transactional_offset:
31
+ consumer_format: 'must respond to #consumer_group_metadata_pointer method'
32
+ message_format: 'must respond to #topic, #partition and #offset'
33
+ missing: must be present
34
+ offset_metadata_format: must be string or nil
35
+
30
36
  test:
31
37
  missing: must be present
32
38
  nested.id_format: 'is invalid'
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.5.1
6
+ image: confluentinc/cp-kafka:7.5.3
7
7
 
8
8
  ports:
9
9
  - 9092:9092
@@ -23,3 +23,5 @@ services:
23
23
  KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
24
24
  KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
25
25
  KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
26
+ KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND: "true"
27
+ KAFKA_AUTHORIZER_CLASS_NAME: org.apache.kafka.metadata.authorizer.StandardAuthorizer
@@ -44,8 +44,6 @@ module WaterDrop
44
44
  def commit_transaction
45
45
  @transaction_level -= 1
46
46
 
47
- return unless @transaction_level.zero?
48
-
49
47
  # Transfer transactional data on success
50
48
  @transaction_topics.each do |topic, messages|
51
49
  @topics[topic] += messages
@@ -58,12 +56,19 @@ module WaterDrop
58
56
  @transaction_active = false
59
57
  end
60
58
 
59
+ # Fakes storing the offset in a transactional fashion
60
+ #
61
+ # @param _consumer [#consumer_group_metadata_pointer] any consumer from which we can obtain
62
+ # the librdkafka consumer group metadata pointer
63
+ # @param _tpl [Rdkafka::Consumer::TopicPartitionList] consumer tpl for offset storage
64
+ # @param _timeout [Integer] ms timeout
65
+ def send_offsets_to_transaction(_consumer, _tpl, _timeout)
66
+ nil
67
+ end
68
+
61
69
  # Aborts the transaction
62
70
  def abort_transaction
63
71
  @transaction_level -= 1
64
-
65
- return unless @transaction_level.zero?
66
-
67
72
  @transaction_topics.clear
68
73
  @transaction_messages.clear
69
74
  @transaction_active = false
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WaterDrop
4
+ module Contracts
5
+ # Contract to ensure that arguments provided to the transactional offset commit are valid
6
+ # and match our expectations
7
+ class TransactionalOffset < ::Karafka::Core::Contractable::Contract
8
+ configure do |config|
9
+ config.error_messages = YAML.safe_load(
10
+ File.read(
11
+ File.join(WaterDrop.gem_root, 'config', 'locales', 'errors.yml')
12
+ )
13
+ ).fetch('en').fetch('validations').fetch('transactional_offset')
14
+ end
15
+
16
+ required(:consumer) { |val| val.respond_to?(:consumer_group_metadata_pointer) }
17
+ required(:message) { |val| val.respond_to?(:topic) && val.respond_to?(:partition) }
18
+ required(:offset_metadata) { |val| val.is_a?(String) || val.nil? }
19
+ end
20
+ end
21
+ end
@@ -25,6 +25,9 @@ module WaterDrop
25
25
  # Raised when we want to send a message that is invalid (impossible topic, etc)
26
26
  MessageInvalidError = Class.new(BaseError)
27
27
 
28
+ # Raised when we want to commit transactional offset and the input is invalid
29
+ TransactionalOffsetInvalidError = Class.new(BaseError)
30
+
28
31
  # Raised when we've got an unexpected status. This should never happen. If it does, please
29
32
  # contact us as it is an error.
30
33
  StatusInvalidError = Class.new(BaseError)
@@ -32,7 +35,13 @@ module WaterDrop
32
35
  # Raised when there is an inline error during single message produce operations
33
36
  ProduceError = Class.new(BaseError)
34
37
 
38
+ # Raised when we attempt to perform operation that is only allowed inside of a transaction and
39
+ # there is no transaction around us
40
+ TransactionRequiredError = Class.new(BaseError)
41
+
35
42
  # Raise it within a transaction to abort it
43
+ # It does not have an `Error` postfix because technically it is not an error as it is used for
44
+ # graceful transaction aborting
36
45
  AbortTransaction = Class.new(BaseError)
37
46
 
38
47
  # Raised when during messages producing something bad happened inline
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WaterDrop
4
+ # Extra internal helper objects
5
+ module Helpers
6
+ # Atomic counter that we can safely increment and decrement without race conditions
7
+ class Counter
8
+ # @return [Integer] current value
9
+ attr_reader :value
10
+
11
+ def initialize
12
+ @value = 0
13
+ @mutex = Mutex.new
14
+ end
15
+
16
+ # Increments the value by 1
17
+ def increment
18
+ @mutex.synchronize { @value += 1 }
19
+ end
20
+
21
+ # Decrements the value by 1
22
+ def decrement
23
+ @mutex.synchronize { @value -= 1 }
24
+ end
25
+ end
26
+ end
27
+ end
@@ -57,7 +57,8 @@ module WaterDrop
57
57
  offset: delivery_report.offset,
58
58
  partition: delivery_report.partition,
59
59
  topic: delivery_report.topic_name,
60
- delivery_report: delivery_report
60
+ delivery_report: delivery_report,
61
+ label: delivery_report.label
61
62
  )
62
63
  end
63
64
 
@@ -71,7 +72,9 @@ module WaterDrop
71
72
  offset: delivery_report.offset,
72
73
  partition: delivery_report.partition,
73
74
  topic: delivery_report.topic_name,
74
- delivery_report: delivery_report
75
+ delivery_report: delivery_report,
76
+ label: delivery_report.label,
77
+ type: 'librdkafka.dispatch_error'
75
78
  )
76
79
  end
77
80
 
@@ -86,6 +89,7 @@ module WaterDrop
86
89
  partition: delivery_report.partition,
87
90
  topic: delivery_report.topic_name,
88
91
  delivery_report: delivery_report,
92
+ label: delivery_report.label,
89
93
  type: 'librdkafka.dispatch_error'
90
94
  )
91
95
  end
@@ -145,6 +145,20 @@ module WaterDrop
145
145
  info(event, 'Committing transaction')
146
146
  end
147
147
 
148
+ # @param event [Dry::Events::Event] event that happened with the details
149
+ def on_transaction_marked_as_consumed(event)
150
+ message = event[:message]
151
+ topic = message.topic
152
+ partition = message.partition
153
+ offset = message.offset
154
+ loc = "#{topic}/#{partition}"
155
+
156
+ info(
157
+ event,
158
+ "Marking message with offset #{offset} for topic #{loc} as consumed in a transaction"
159
+ )
160
+ end
161
+
148
162
  # @param event [Dry::Events::Event] event that happened with the details
149
163
  def on_transaction_finished(event)
150
164
  info(event, 'Processing transaction')
@@ -22,6 +22,7 @@ module WaterDrop
22
22
  transaction.started
23
23
  transaction.committed
24
24
  transaction.aborted
25
+ transaction.marked_as_consumed
25
26
  transaction.finished
26
27
 
27
28
  buffer.flushed_async
@@ -82,7 +82,7 @@ module WaterDrop
82
82
 
83
83
  @buffer_mutex.synchronize do
84
84
  data_for_dispatch = @messages
85
- @messages = Concurrent::Array.new
85
+ @messages = []
86
86
  end
87
87
 
88
88
  # Do nothing if nothing to flush
@@ -4,6 +4,11 @@ module WaterDrop
4
4
  class Producer
5
5
  # Transactions related producer functionalities
6
6
  module Transactions
7
+ # Contract to validate that input for transactional offset storage is correct
8
+ CONTRACT = Contracts::TransactionalOffset.new
9
+
10
+ private_constant :CONTRACT
11
+
7
12
  # Creates a transaction.
8
13
  #
9
14
  # Karafka transactions work in a similar manner to SQL db transactions though there are some
@@ -91,6 +96,49 @@ module WaterDrop
91
96
  @transactional = config.kafka.to_h.key?(:'transactional.id')
92
97
  end
93
98
 
99
+ # Marks given message as consumed inside of a transaction.
100
+ #
101
+ # @param consumer [#consumer_group_metadata_pointer] any consumer from which we can obtain
102
+ # the librdkafka consumer group metadata pointer
103
+ # @param message [Karafka::Messages::Message] karafka message
104
+ # @param offset_metadata [String] offset metadata or nil if none
105
+ def transaction_mark_as_consumed(consumer, message, offset_metadata = nil)
106
+ raise Errors::TransactionRequiredError unless @transaction_mutex.owned?
107
+
108
+ CONTRACT.validate!(
109
+ {
110
+ consumer: consumer,
111
+ message: message,
112
+ offset_metadata: offset_metadata
113
+ },
114
+ Errors::TransactionalOffsetInvalidError
115
+ )
116
+
117
+ details = { message: message, offset_metadata: offset_metadata }
118
+
119
+ transactional_instrument(:marked_as_consumed, details) do
120
+ tpl = Rdkafka::Consumer::TopicPartitionList.new
121
+ partition = Rdkafka::Consumer::Partition.new(
122
+ message.partition,
123
+ # +1 because this is next offset from which we will start processing from
124
+ message.offset + 1,
125
+ 0,
126
+ offset_metadata
127
+ )
128
+
129
+ tpl.add_topic_and_partitions_with_offsets(message.topic, [partition])
130
+
131
+ with_transactional_error_handling(:store_offset) do
132
+ client.send_offsets_to_transaction(
133
+ consumer,
134
+ tpl,
135
+ # This setting is at the moment in seconds and we require ms
136
+ @config.max_wait_timeout * 1_000
137
+ )
138
+ end
139
+ end
140
+ end
141
+
94
142
  private
95
143
 
96
144
  # Runs provided code with a transaction wrapper if transactions are enabled.
@@ -105,9 +153,10 @@ module WaterDrop
105
153
  # Instruments the transactional operation with producer id
106
154
  #
107
155
  # @param key [Symbol] transaction operation key
156
+ # @param details [Hash] additional instrumentation details
108
157
  # @param block [Proc] block to run inside the instrumentation or nothing if not given
109
- def transactional_instrument(key, &block)
110
- @monitor.instrument("transaction.#{key}", producer_id: id, &block)
158
+ def transactional_instrument(key, details = EMPTY_HASH, &block)
159
+ @monitor.instrument("transaction.#{key}", details.merge(producer_id: id), &block)
111
160
  end
112
161
 
113
162
  # Error handling for transactional operations is a bit special. There are three types of
@@ -157,7 +206,7 @@ module WaterDrop
157
206
  # Always attempt to abort but if aborting fails with an abortable error, do not attempt
158
207
  # to abort from abort as this could create an infinite loop
159
208
  with_transactional_error_handling(:abort, allow_abortable: false) do
160
- transactional_instrument(:aborted) { @client.abort_transaction }
209
+ transactional_instrument(:aborted) { client.abort_transaction }
161
210
  end
162
211
 
163
212
  raise
@@ -16,7 +16,10 @@ module WaterDrop
16
16
  Rdkafka::Producer::DeliveryHandle::WaitTimeoutError
17
17
  ].freeze
18
18
 
19
- private_constant :SUPPORTED_FLOW_ERRORS
19
+ # Empty has to save on memory allocations
20
+ EMPTY_HASH = {}.freeze
21
+
22
+ private_constant :SUPPORTED_FLOW_ERRORS, :EMPTY_HASH
20
23
 
21
24
  def_delegators :config, :middleware
22
25
 
@@ -24,7 +27,7 @@ module WaterDrop
24
27
  attr_reader :id
25
28
  # @return [Status] producer status object
26
29
  attr_reader :status
27
- # @return [Concurrent::Array] internal messages buffer
30
+ # @return [Array] internal messages buffer
28
31
  attr_reader :messages
29
32
  # @return [Object] monitor we want to use
30
33
  attr_reader :monitor
@@ -35,14 +38,14 @@ module WaterDrop
35
38
  # @param block [Proc] configuration block
36
39
  # @return [Producer] producer instance
37
40
  def initialize(&block)
38
- @operations_in_progress = Concurrent::AtomicFixnum.new(0)
41
+ @operations_in_progress = Helpers::Counter.new
39
42
  @buffer_mutex = Mutex.new
40
43
  @connecting_mutex = Mutex.new
41
44
  @operating_mutex = Mutex.new
42
45
  @transaction_mutex = Mutex.new
43
46
 
44
47
  @status = Status.new
45
- @messages = Concurrent::Array.new
48
+ @messages = []
46
49
 
47
50
  return unless block
48
51
 
@@ -127,7 +130,7 @@ module WaterDrop
127
130
  def purge
128
131
  @monitor.instrument('buffer.purged', producer_id: id) do
129
132
  @buffer_mutex.synchronize do
130
- @messages = Concurrent::Array.new
133
+ @messages = []
131
134
  end
132
135
 
133
136
  @client.purge
@@ -3,5 +3,5 @@
3
3
  # WaterDrop library
4
4
  module WaterDrop
5
5
  # Current WaterDrop version
6
- VERSION = '2.6.11'
6
+ VERSION = '2.6.12'
7
7
  end
data/lib/waterdrop.rb CHANGED
@@ -9,7 +9,6 @@
9
9
  securerandom
10
10
  karafka-core
11
11
  pathname
12
- concurrent/atomic/atomic_fixnum
13
12
  ].each { |lib| require lib }
14
13
 
15
14
  # WaterDrop library
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: waterdrop
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.11
4
+ version: 2.6.12
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: 2023-10-25 00:00:00.000000000 Z
38
+ date: 2024-01-03 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: karafka-core
@@ -102,7 +102,9 @@ files:
102
102
  - lib/waterdrop/contracts.rb
103
103
  - lib/waterdrop/contracts/config.rb
104
104
  - lib/waterdrop/contracts/message.rb
105
+ - lib/waterdrop/contracts/transactional_offset.rb
105
106
  - lib/waterdrop/errors.rb
107
+ - lib/waterdrop/helpers/counter.rb
106
108
  - lib/waterdrop/instrumentation/callbacks/delivery.rb
107
109
  - lib/waterdrop/instrumentation/callbacks/error.rb
108
110
  - lib/waterdrop/instrumentation/callbacks/statistics.rb
@@ -149,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
151
  - !ruby/object:Gem::Version
150
152
  version: '0'
151
153
  requirements: []
152
- rubygems_version: 3.4.19
154
+ rubygems_version: 3.5.3
153
155
  signing_key:
154
156
  specification_version: 4
155
157
  summary: Kafka messaging made easy!
metadata.gz.sig CHANGED
Binary file