waterdrop 2.10.4 → 2.10.5

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: 32f1c3b166f74db06dc75080800f4972207facc943d00d7eeab73a6c33668aed
4
- data.tar.gz: 8de1c8eecf566e241bc375fea7de5f568cd055be35a83effcf5de23b7c92f468
3
+ metadata.gz: d61c97700e2882107b4e3309176c22fc85d06df909e3f11f0779bb33cab0ec96
4
+ data.tar.gz: 1b4c0e3c7ac54d6b86905544396865934ee783ba730dc0ccc6b3bd6b954a7ce8
5
5
  SHA512:
6
- metadata.gz: 901c6f6a576c38807eee47641675b12f9ef3907325fb2435c6b2e8656724c4f57be6593a10b7b85348b8cadd317216fe84ba182a9ccfaf3fa0208119c0609686
7
- data.tar.gz: 25720c9a6ac31520400f181af47b389eae9def35f384cf9b1d89f8466c4cafc904c0390fbe14a1222c6917b3b26fbcc38d9580e9a0fa45db07d1cc9528c3bb85
6
+ metadata.gz: 9d509ae8aafda829f217357e6d8447905910137a4af850cd8d4bac7a35bb3af703432339c05fa45117dd8acff2a481718f18a94dd7e7ea0ba0259a86b6ae8d42
7
+ data.tar.gz: 4a8c01eb9aa11c49eb810888a65a520731298ea500bdcaa2c0b78bc163ffc346cc3975f8cad06ea7e50c9c9d21357457eaf745269b35e598a9eccebaf611e7a4
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 4.0.6
1
+ 4.0.7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # WaterDrop changelog
2
2
 
3
+ ## 2.10.5 (2026-09-23)
4
+ - [Fix] Stop `#flush` from running middleware twice on messages re-buffered after a failed flush (regression of #474 reintroduced by the failure/requeue path), which double-encrypted/compressed/serialized payloads and duplicated headers on retry.
5
+ - [Fix] Re-check the producer liveness under `@buffer_mutex` in `#buffer`/`#buffer_many`, so a producer closed between the liveness check and the append raises `ProducerClosedError` instead of accepting the message into a closed producer's buffer and losing it silently.
6
+ - [Fix] Restore both buffers when `#flush` fails before anything is dispatched (a middleware step raising, for example) instead of discarding the whole batch. Only `ProduceManyError` and `MessageInvalidError` were covered before.
7
+
3
8
  ## 2.10.4 (2026-08-26)
4
9
  - [Fix] Avoid a `FrozenError` on `Producer#close` when the producer is configured with a frozen string id (for example a frozen string literal, `config.id = "rspec"`).
5
10
  - [Fix] Emit `connection_pool.shutdown` only once when shutting down the global connection pool.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- waterdrop (2.10.4)
4
+ waterdrop (2.10.5)
5
5
  karafka-core (>= 2.5.12, < 3.0.0)
6
6
  karafka-rdkafka (>= 0.24.0)
7
7
  zeitwerk (~> 2.3)
@@ -14,9 +14,9 @@ GEM
14
14
  connection_pool (3.0.2)
15
15
  drb (2.2.3)
16
16
  ffi (1.17.4)
17
- io-console (0.8.2)
17
+ io-console (0.9.2)
18
18
  json (2.21.2)
19
- karafka-core (2.6.2)
19
+ karafka-core (2.6.3)
20
20
  karafka-rdkafka (>= 0.20.0)
21
21
  logger (>= 1.6.0)
22
22
  karafka-rdkafka (0.28.0)
@@ -35,10 +35,10 @@ GEM
35
35
  ostruct (0.6.3)
36
36
  prism (1.9.0)
37
37
  rake (13.4.2)
38
- reline (0.6.3)
38
+ reline (0.7.0)
39
39
  io-console (~> 0.5)
40
40
  ruby2_keywords (0.0.5)
41
- simplecov (1.1.1)
41
+ simplecov (1.3.0)
42
42
  warning (1.6.0)
43
43
  zeitwerk (2.8.3)
44
44
 
@@ -61,9 +61,9 @@ CHECKSUMS
61
61
  connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
62
62
  drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
63
63
  ffi (1.17.4) sha256=bcd1642e06f0d16fc9e09ac6d49c3a7298b9789bcb58127302f934e437d60acf
64
- io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
64
+ io-console (0.9.2) sha256=efa74f891dd03c0939a931dfc6e74c2813d904763d456ea9762b0525e748db08
65
65
  json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
66
- karafka-core (2.6.2) sha256=c2fd7f277201b8ca97b824b364ad76bf776b8f5527bc422dafb71f7ae48d3a13
66
+ karafka-core (2.6.3) sha256=83a783895d8f69e5fd4b7e77617f6a43c129bbb905c93e1babb9ff299470ba4a
67
67
  karafka-rdkafka (0.28.0) sha256=fd845140c42341c5892215cac1e8adacda53b0461688f2e16379d82cb33eb006
68
68
  logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
69
69
  mini_portile2 (2.8.9) sha256=0cd7c7f824e010c072e33f68bc02d85a00aeb6fce05bb4819c03dfd3c140c289
@@ -72,11 +72,11 @@ CHECKSUMS
72
72
  ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912
73
73
  prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
74
74
  rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
75
- reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
75
+ reline (0.7.0) sha256=5b012d8e55dbf9d450f12bde2cf7d15ff546ae80b3f8f3b30e570d431815583d
76
76
  ruby2_keywords (0.0.5) sha256=ffd13740c573b7301cf7a2e61fc857b2a8e3d3aff32545d6f8300d8bae10e3ef
77
- simplecov (1.1.1) sha256=25825ef13f0b2e74694d769817dad6ab8e90131dabdaa666e522fea105521e78
77
+ simplecov (1.3.0) sha256=d9886307863c1ead47657dcbed869bfffd82318808b09014d8f0ff77dcfe7492
78
78
  warning (1.6.0) sha256=a49cdfae19fb77d19afff2efbe45f8ab759e9cd25b4e4ce2c79dbaf46bdb6c9e
79
- waterdrop (2.10.4)
79
+ waterdrop (2.10.5)
80
80
  zeitwerk (2.8.3) sha256=2c85125a8467ce069e20123d1e709a08955c9d29c118c25b46b7b7fafdbb92e5
81
81
 
82
82
  BUNDLED WITH
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # WaterDrop
2
2
 
3
- [![Build Status](https://github.com/karafka/waterdrop/workflows/ci/badge.svg)](https://github.com/karafka/waterdrop/actions?query=workflow%3Aci)
4
- [![Gem Version](https://badge.fury.io/rb/waterdrop.svg)](http://badge.fury.io/rb/waterdrop)
3
+ [![Build Status](https://github.com/karafka/waterdrop/actions/workflows/ci.yml/badge.svg)](https://github.com/karafka/waterdrop/actions/workflows/ci.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/waterdrop.svg)](https://badge.fury.io/rb/waterdrop)
5
5
  [![Join the chat at https://slack.karafka.io](https://raw.githubusercontent.com/karafka/misc/master/slack.svg)](https://slack.karafka.io)
6
6
 
7
7
  WaterDrop is a standalone gem that sends messages to Kafka easily with an extra validation layer. It is a part of the [Karafka](https://github.com/karafka/karafka) ecosystem.
@@ -4,6 +4,11 @@
4
4
  # Only specified warnings are allowed, all others should trigger failure
5
5
 
6
6
  allowed_patterns=(
7
+ # Enabling share groups (KIP-932) requires "share" in group.coordinator.rebalance.protocols
8
+ # on cp-kafka 8.3.0 (Kafka 4.x). That mechanism is deprecated in favour of the share.version
9
+ # feature flag (Kafka 5.0), but is still functionally required to enable share groups in this
10
+ # version, so the broker logs this deprecation warning on every startup. Expected here.
11
+ "group.coordinator.rebalance.protocols. is deprecated"
7
12
  "Performing controller activation"
8
13
  "registered with feature metadata.version"
9
14
  "TOPIC_ALREADY_EXISTS"
@@ -49,6 +49,9 @@ services:
49
49
  KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
50
50
  KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
51
51
  KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
52
+ KAFKA_GROUP_COORDINATOR_REBALANCE_PROTOCOLS: 'classic,consumer,share'
53
+ KAFKA_SHARE_COORDINATOR_STATE_TOPIC_REPLICATION_FACTOR: 1
54
+ KAFKA_SHARE_COORDINATOR_STATE_TOPIC_MIN_ISR: 1
52
55
  # Required by Confluent image when SASL is enabled
53
56
  # Also allow HTTP URLs for JWKS endpoint (Kafka 3.0+ blocks non-HTTPS by default)
54
57
  KAFKA_OPTS: "-Djava.security.auth.login.config=/etc/kafka/kafka_server_jaas.conf -Dorg.apache.kafka.sasl.oauthbearer.allowed.urls=http://keycloak:8080/realms/kafka/protocol/openid-connect/certs"
@@ -21,6 +21,9 @@ services:
21
21
  KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
22
22
  KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
23
23
  KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
24
+ KAFKA_GROUP_COORDINATOR_REBALANCE_PROTOCOLS: 'classic,consumer,share'
25
+ KAFKA_SHARE_COORDINATOR_STATE_TOPIC_REPLICATION_FACTOR: 1
26
+ KAFKA_SHARE_COORDINATOR_STATE_TOPIC_MIN_ISR: 1
24
27
  KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND: "true"
25
28
  KAFKA_AUTHORIZER_CLASS_NAME: org.apache.kafka.metadata.authorizer.StandardAuthorizer
26
29
  KAFKA_OPTS: ""
data/docker-compose.yml CHANGED
@@ -21,5 +21,8 @@ services:
21
21
  KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
22
22
  KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
23
23
  KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
24
+ KAFKA_GROUP_COORDINATOR_REBALANCE_PROTOCOLS: 'classic,consumer,share'
25
+ KAFKA_SHARE_COORDINATOR_STATE_TOPIC_REPLICATION_FACTOR: 1
26
+ KAFKA_SHARE_COORDINATOR_STATE_TOPIC_MIN_ISR: 1
24
27
  KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND: "true"
25
28
  KAFKA_AUTHORIZER_CLASS_NAME: org.apache.kafka.metadata.authorizer.StandardAuthorizer
@@ -44,15 +44,18 @@ module WaterDrop
44
44
  #
45
45
  # @param messages [Array<Hash>] array with messages that comply with the
46
46
  # {Contracts::Message} contract
47
+ # @param run_middleware [Boolean] should the middleware chain be run on the messages. Set to
48
+ # false only when the messages have already been through middleware (for example when
49
+ # re-dispatching a re-buffered flush batch) so it is not applied twice.
47
50
  #
48
51
  # @return [Array<Rdkafka::Producer::DeliveryHandle>] deliveries handles
49
52
  #
50
53
  # @raise [Rdkafka::RdkafkaError] When adding the messages to rdkafka's queue failed
51
54
  # @raise [Errors::MessageInvalidError] When any of the provided messages details are invalid
52
55
  # and the message could not be sent to Kafka
53
- def produce_many_async(messages)
56
+ def produce_many_async(messages, run_middleware: true)
54
57
  dispatched = []
55
- messages = middleware.run_many(messages)
58
+ messages = middleware.run_many(messages) if run_middleware
56
59
  messages.each { |message| validate_message!(message) }
57
60
 
58
61
  @monitor.instrument(
@@ -15,12 +15,22 @@ module WaterDrop
15
15
  # The append runs under @buffer_mutex because flush/purge/close swap @messages for a fresh
16
16
  # array under the same lock. Without it, a concurrent swap between reading @messages and
17
17
  # appending would land the message in the orphaned old array and silently lose it.
18
+ #
19
+ # The liveness check is repeated under the same lock because `close` marks the producer
20
+ # `:closing` before its final flush acquires @buffer_mutex. Checking only above the lock
21
+ # would let a close complete in between, stranding the message in a closed producer's
22
+ # buffer while this call returned success.
18
23
  @monitor.instrument(
19
24
  "message.buffered",
20
25
  producer_id: id,
21
26
  message: message,
22
27
  buffer: @messages
23
- ) { @buffer_mutex.synchronize { @messages << message } }
28
+ ) do
29
+ @buffer_mutex.synchronize do
30
+ ensure_active!
31
+ @messages << message
32
+ end
33
+ end
24
34
  end
25
35
 
26
36
  # Adds given messages into the internal producer buffer without flushing them to Kafka
@@ -34,14 +44,20 @@ module WaterDrop
34
44
 
35
45
  # The concat runs under @buffer_mutex for the same reason as #buffer: flush/purge/close swap
36
46
  # @messages under the lock, so an unguarded concat could append into an array that has just
37
- # been captured for dispatch (or discarded), silently losing the messages.
47
+ # been captured for dispatch (or discarded), silently losing the messages. The liveness
48
+ # check is repeated under the lock for the same reason as in #buffer: a close that
49
+ # completes between the check above and the lock would otherwise strand the batch.
38
50
  @monitor.instrument(
39
51
  "messages.buffered",
40
52
  producer_id: id,
41
53
  messages: messages,
42
54
  buffer: @messages
43
55
  ) do
44
- @buffer_mutex.synchronize { @messages.concat(messages) }
56
+ @buffer_mutex.synchronize do
57
+ ensure_active!
58
+ @messages.concat(messages)
59
+ end
60
+
45
61
  messages
46
62
  end
47
63
  end
@@ -53,7 +69,7 @@ module WaterDrop
53
69
  @monitor.instrument(
54
70
  "buffer.flushed_async",
55
71
  producer_id: id,
56
- messages: @messages
72
+ messages: messages
57
73
  ) { flush(false) }
58
74
  end
59
75
 
@@ -64,7 +80,7 @@ module WaterDrop
64
80
  @monitor.instrument(
65
81
  "buffer.flushed_sync",
66
82
  producer_id: id,
67
- messages: @messages
83
+ messages: messages
68
84
  ) { flush(true) }
69
85
  end
70
86
 
@@ -78,17 +94,47 @@ module WaterDrop
78
94
  # @note We use this method underneath to provide a different instrumentation for sync and
79
95
  # async flushing within the public API
80
96
  def flush(sync)
81
- data_for_dispatch = nil
97
+ fresh = nil
98
+ requeued = nil
99
+ data_for_dispatch = []
82
100
 
83
101
  @buffer_mutex.synchronize do
84
- data_for_dispatch = @messages
102
+ requeued = @requeued
103
+ @requeued = []
104
+ fresh = @messages
85
105
  @messages = []
86
106
  end
87
107
 
108
+ # Middleware runs exactly once per message: on the fresh messages here, never again on the
109
+ # already-transformed ones coming back from a failed flush, which a second pass would
110
+ # corrupt.
111
+ #
112
+ # A middleware step raising here (a serializer, encryptor or schema lookup failing) would
113
+ # otherwise destroy both buffers, since they were emptied above and nothing has been
114
+ # dispatched yet to recover them from. Messages are run one by one so that on failure the
115
+ # ones whose chain completed are kept as returned and are not transformed again, while the
116
+ # failing one and those after it stay on the middleware path. The failing one is not
117
+ # requeued because that would dispatch it without the rest of its chain.
118
+ processed = []
119
+
120
+ begin
121
+ fresh.each { |message| processed << middleware.run(message) }
122
+ rescue
123
+ restore_undispatched(fresh.drop(processed.size), requeued.concat(processed))
124
+
125
+ raise
126
+ end
127
+
128
+ data_for_dispatch = requeued.concat(processed)
129
+
88
130
  # Do nothing if nothing to flush
89
131
  return data_for_dispatch if data_for_dispatch.empty?
90
132
 
91
- sync ? produce_many_sync(data_for_dispatch) : produce_many_async(data_for_dispatch)
133
+ if sync
134
+ produce_many_sync(data_for_dispatch, run_middleware: false)
135
+ else
136
+ produce_many_async(data_for_dispatch, run_middleware: false)
137
+ end
92
138
  rescue Errors::ProduceManyError => e
93
139
  # A dispatch failed partway through the batch. Re-buffer the messages that never reached
94
140
  # librdkafka so a partial failure does not silently drop valid buffered messages. For a
@@ -106,15 +152,27 @@ module WaterDrop
106
152
  raise
107
153
  end
108
154
 
109
- # Puts not-yet-dispatched messages back at the front of the buffer (preserving their original
110
- # order relative to each other and to anything buffered concurrently), so a failed flush does
111
- # not lose them.
155
+ # Puts both sets back after a failure that happened before anything reached librdkafka. They
156
+ # go to different buffers because middleware has run on one of them and not the other, which
157
+ # is what keeps it applied exactly once per message.
158
+ #
159
+ # @param fresh [Array<Hash>] messages whose middleware chain has not completed
160
+ # @param requeued [Array<Hash>] already middleware-processed messages
161
+ def restore_undispatched(fresh, requeued)
162
+ @buffer_mutex.synchronize do
163
+ @messages.unshift(*fresh)
164
+ @requeued.unshift(*requeued)
165
+ end
166
+ end
167
+
168
+ # Puts not-yet-dispatched messages back at the front of the retry buffer (preserving their
169
+ # original order relative to each other), so a failed flush does not lose them.
112
170
  #
113
- # @param messages [Array<Hash>] messages to restore to the buffer
171
+ # @param messages [Array<Hash>] already middleware-processed messages to restore
114
172
  def requeue_unflushed(messages)
115
173
  return if messages.empty?
116
174
 
117
- @buffer_mutex.synchronize { @messages.unshift(*messages) }
175
+ @buffer_mutex.synchronize { @requeued.unshift(*messages) }
118
176
  end
119
177
  end
120
178
  end
@@ -48,6 +48,9 @@ module WaterDrop
48
48
  #
49
49
  # @param messages [Array<Hash>] array with messages that comply with the
50
50
  # {Contracts::Message} contract
51
+ # @param run_middleware [Boolean] should the middleware chain be run on the messages. Set to
52
+ # false only when the messages have already been through middleware (for example when
53
+ # re-dispatching a re-buffered flush batch) so it is not applied twice.
51
54
  #
52
55
  # @return [Array<Rdkafka::Producer::DeliveryHandle>] delivery handles for messages that were
53
56
  # sent (can be used to verify offset, partition, etc via `#create_result`)
@@ -63,8 +66,8 @@ module WaterDrop
63
66
  # errors), returning handles guarantees the same type in the `dispatched` collection,
64
67
  # allowing uniform error handling. Each handle is in its final state after this method
65
68
  # returns, so you can call `handle.create_result` to obtain the delivery report if needed.
66
- def produce_many_sync(messages)
67
- messages = middleware.run_many(messages)
69
+ def produce_many_sync(messages, run_middleware: true)
70
+ messages = middleware.run_many(messages) if run_middleware
68
71
  messages.each { |message| validate_message!(message) }
69
72
 
70
73
  dispatched = []
@@ -39,8 +39,6 @@ module WaterDrop
39
39
  attr_reader :id
40
40
  # @return [Status] producer status object
41
41
  attr_reader :status
42
- # @return [Array] internal messages buffer
43
- attr_reader :messages
44
42
  # @return [Object] monitor we want to use
45
43
  attr_reader :monitor
46
44
  # @return [Object] dry-configurable config object
@@ -71,6 +69,9 @@ module WaterDrop
71
69
 
72
70
  @status = Status.new
73
71
  @messages = []
72
+ # Messages re-buffered after a failed flush. Kept apart from @messages because they have
73
+ # already passed through middleware and must not be transformed again on the next flush.
74
+ @requeued = []
74
75
 
75
76
  # Instrument producer creation for global listeners
76
77
  class_monitor.instrument(
@@ -177,6 +178,12 @@ module WaterDrop
177
178
  @client
178
179
  end
179
180
 
181
+ # @return [Array] internal messages buffer awaiting a flush. Includes both freshly buffered
182
+ # messages and any messages re-buffered after a failed flush that are waiting to be retried.
183
+ def messages
184
+ @requeued + @messages
185
+ end
186
+
180
187
  # Returns the number of messages in the librdkafka producer queue.
181
188
  #
182
189
  # This count includes:
@@ -232,6 +239,7 @@ module WaterDrop
232
239
  @monitor.instrument("buffer.purged", producer_id: id) do
233
240
  @buffer_mutex.synchronize do
234
241
  @messages = []
242
+ @requeued = []
235
243
  end
236
244
 
237
245
  # We should not purge if there is no client initialized
@@ -321,6 +329,7 @@ module WaterDrop
321
329
  return false unless @client
322
330
  return false unless @status.connected?
323
331
  return false unless @messages.empty?
332
+ return false unless @requeued.empty?
324
333
  return false unless @operations_in_progress.value.zero?
325
334
 
326
335
  @status.disconnecting!
@@ -354,6 +363,7 @@ module WaterDrop
354
363
  return false unless @client
355
364
  return false unless @status.connected?
356
365
  return false unless @messages.empty?
366
+ return false unless @requeued.empty?
357
367
  return false if @transaction_mutex.locked?
358
368
  return false if @operating_mutex.locked?
359
369
 
@@ -503,7 +513,7 @@ module WaterDrop
503
513
  # Try to get buffer info safely
504
514
  if @buffer_mutex.try_lock
505
515
  begin
506
- parts << "buffer_size=#{@messages.size}"
516
+ parts << "buffer_size=#{@messages.size + @requeued.size}"
507
517
  ensure
508
518
  @buffer_mutex.unlock
509
519
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module WaterDrop
4
4
  # Current WaterDrop version
5
- VERSION = "2.10.4"
5
+ VERSION = "2.10.5"
6
6
  end
data/package-lock.json CHANGED
@@ -286,9 +286,9 @@
286
286
  }
287
287
  },
288
288
  "node_modules/smol-toml": {
289
- "version": "1.7.1",
290
- "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.1.tgz",
291
- "integrity": "sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ==",
289
+ "version": "1.8.0",
290
+ "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.8.0.tgz",
291
+ "integrity": "sha512-kCZr2V3ch9i00x8zXRhjUNVcjG9ijES5dDudkXvUVCT5QlJNQWElSJdZqyPemffHoLNUYwOcou0Fy+ojN0uHSQ==",
292
292
  "dev": true,
293
293
  "license": "BSD-3-Clause",
294
294
  "engines": {
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.10.4
4
+ version: 2.10.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Mensfeld
@@ -160,7 +160,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
160
160
  - !ruby/object:Gem::Version
161
161
  version: '0'
162
162
  requirements: []
163
- rubygems_version: 4.0.16
163
+ rubygems_version: 4.0.20
164
164
  specification_version: 4
165
165
  summary: Kafka messaging made easy!
166
166
  test_files: []