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 +4 -4
- data/.ruby-version +1 -1
- data/CHANGELOG.md +5 -0
- data/Gemfile.lock +10 -10
- data/README.md +2 -2
- data/bin/verify_kafka_warnings +5 -0
- data/docker-compose.oauth.yml +3 -0
- data/docker-compose.sasl.yml +3 -0
- data/docker-compose.yml +3 -0
- data/lib/waterdrop/producer/async.rb +5 -2
- data/lib/waterdrop/producer/buffer.rb +71 -13
- data/lib/waterdrop/producer/sync.rb +5 -2
- data/lib/waterdrop/producer.rb +13 -3
- data/lib/waterdrop/version.rb +1 -1
- data/package-lock.json +3 -3
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d61c97700e2882107b4e3309176c22fc85d06df909e3f11f0779bb33cab0ec96
|
|
4
|
+
data.tar.gz: 1b4c0e3c7ac54d6b86905544396865934ee783ba730dc0ccc6b3bd6b954a7ce8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9d509ae8aafda829f217357e6d8447905910137a4af850cd8d4bac7a35bb3af703432339c05fa45117dd8acff2a481718f18a94dd7e7ea0ba0259a86b6ae8d42
|
|
7
|
+
data.tar.gz: 4a8c01eb9aa11c49eb810888a65a520731298ea500bdcaa2c0b78bc163ffc346cc3975f8cad06ea7e50c9c9d21357457eaf745269b35e598a9eccebaf611e7a4
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.0.
|
|
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
|
+
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.
|
|
17
|
+
io-console (0.9.2)
|
|
18
18
|
json (2.21.2)
|
|
19
|
-
karafka-core (2.6.
|
|
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.
|
|
38
|
+
reline (0.7.0)
|
|
39
39
|
io-console (~> 0.5)
|
|
40
40
|
ruby2_keywords (0.0.5)
|
|
41
|
-
simplecov (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.
|
|
64
|
+
io-console (0.9.2) sha256=efa74f891dd03c0939a931dfc6e74c2813d904763d456ea9762b0525e748db08
|
|
65
65
|
json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
|
|
66
|
-
karafka-core (2.6.
|
|
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.
|
|
75
|
+
reline (0.7.0) sha256=5b012d8e55dbf9d450f12bde2cf7d15ff546ae80b3f8f3b30e570d431815583d
|
|
76
76
|
ruby2_keywords (0.0.5) sha256=ffd13740c573b7301cf7a2e61fc857b2a8e3d3aff32545d6f8300d8bae10e3ef
|
|
77
|
-
simplecov (1.
|
|
77
|
+
simplecov (1.3.0) sha256=d9886307863c1ead47657dcbed869bfffd82318808b09014d8f0ff77dcfe7492
|
|
78
78
|
warning (1.6.0) sha256=a49cdfae19fb77d19afff2efbe45f8ab759e9cd25b4e4ce2c79dbaf46bdb6c9e
|
|
79
|
-
waterdrop (2.10.
|
|
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
|
-
[](https://github.com/karafka/waterdrop/actions
|
|
4
|
-
[](
|
|
3
|
+
[](https://github.com/karafka/waterdrop/actions/workflows/ci.yml)
|
|
4
|
+
[](https://badge.fury.io/rb/waterdrop)
|
|
5
5
|
[](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.
|
data/bin/verify_kafka_warnings
CHANGED
|
@@ -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"
|
data/docker-compose.oauth.yml
CHANGED
|
@@ -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"
|
data/docker-compose.sasl.yml
CHANGED
|
@@ -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
|
-
)
|
|
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
|
|
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:
|
|
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:
|
|
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
|
-
|
|
97
|
+
fresh = nil
|
|
98
|
+
requeued = nil
|
|
99
|
+
data_for_dispatch = []
|
|
82
100
|
|
|
83
101
|
@buffer_mutex.synchronize do
|
|
84
|
-
|
|
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
|
|
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
|
|
110
|
-
#
|
|
111
|
-
#
|
|
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
|
|
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 { @
|
|
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 = []
|
data/lib/waterdrop/producer.rb
CHANGED
|
@@ -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
|
data/lib/waterdrop/version.rb
CHANGED
data/package-lock.json
CHANGED
|
@@ -286,9 +286,9 @@
|
|
|
286
286
|
}
|
|
287
287
|
},
|
|
288
288
|
"node_modules/smol-toml": {
|
|
289
|
-
"version": "1.
|
|
290
|
-
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.
|
|
291
|
-
"integrity": "sha512-
|
|
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
|
+
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.
|
|
163
|
+
rubygems_version: 4.0.20
|
|
164
164
|
specification_version: 4
|
|
165
165
|
summary: Kafka messaging made easy!
|
|
166
166
|
test_files: []
|