rdkafka 0.13.0.beta.1 → 0.13.0.beta.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -1
- data/lib/rdkafka/admin.rb +7 -2
- data/lib/rdkafka/bindings.rb +17 -4
- data/lib/rdkafka/config.rb +4 -3
- data/lib/rdkafka/consumer/headers.rb +22 -7
- data/lib/rdkafka/consumer/message.rb +1 -1
- data/lib/rdkafka/consumer.rb +21 -22
- data/lib/rdkafka/native_kafka.rb +29 -19
- data/lib/rdkafka/producer.rb +14 -2
- data/lib/rdkafka/version.rb +1 -1
- data/spec/rdkafka/consumer/headers_spec.rb +62 -0
- data/spec/rdkafka/consumer/message_spec.rb +1 -1
- data/spec/rdkafka/consumer_spec.rb +74 -15
- data/spec/rdkafka/metadata_spec.rb +1 -1
- data/spec/rdkafka/native_kafka_spec.rb +14 -4
- data/spec/rdkafka/producer_spec.rb +21 -17
- data/spec/spec_helper.rb +14 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9db414f84847b884bded4eb7643ead52ee664256acdde47dcf7bb5e8421b5882
|
4
|
+
data.tar.gz: d696bce3413d5a591542e6bb1b7191acabd5b331e4115fb7a78b6d65320874d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd209306c840710661108a357adbeec6a24cc1aa1a828959041a0810c25b19ddbb9b52442a69c389995361c96c075a9a572b532cc6ba97c70160d99db46f5d8d
|
7
|
+
data.tar.gz: 5cb50b3717ab7904d4d8afb46233f0435cf519511755ee5137ccdf638775947f23db3ff40ac1efcd0562a767e23f5a65224e8d59f3a753b33206253f9db4dfc6
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
# 0.13.0
|
2
|
+
* Support cooperative sticky partition assignment in the rebalance callback (methodmissing)
|
3
|
+
* Support both string and symbol header keys (ColinDKelley)
|
4
|
+
* Handle tombstone messages properly (kgalieva)
|
2
5
|
* Add topic name to delivery report (maeve)
|
3
6
|
* Allow string partitioner config (mollyegibson)
|
4
7
|
* Fix documented type for DeliveryReport#error (jimmydo)
|
@@ -6,7 +9,7 @@
|
|
6
9
|
* Use finalizers to cleanly exit producer and admin (thijsc)
|
7
10
|
|
8
11
|
# 0.12.0
|
9
|
-
*
|
12
|
+
* Bumps librdkafka to 1.9.0
|
10
13
|
* Fix crash on empty partition key (mensfeld)
|
11
14
|
* Pass the delivery handle to the callback (gvisokinskas)
|
12
15
|
|
data/lib/rdkafka/admin.rb
CHANGED
@@ -18,11 +18,16 @@ module Rdkafka
|
|
18
18
|
|
19
19
|
# Close this admin instance
|
20
20
|
def close
|
21
|
+
return if closed?
|
21
22
|
ObjectSpace.undefine_finalizer(self)
|
22
|
-
|
23
23
|
@native_kafka.close
|
24
24
|
end
|
25
25
|
|
26
|
+
# Whether this admin has closed
|
27
|
+
def closed?
|
28
|
+
@native_kafka.closed?
|
29
|
+
end
|
30
|
+
|
26
31
|
# Create a topic with the given partition count and replication factor
|
27
32
|
#
|
28
33
|
# @raise [ConfigError] When the partition count or replication factor are out of valid range
|
@@ -149,7 +154,7 @@ module Rdkafka
|
|
149
154
|
|
150
155
|
private
|
151
156
|
def closed_admin_check(method)
|
152
|
-
raise Rdkafka::ClosedAdminError.new(method) if
|
157
|
+
raise Rdkafka::ClosedAdminError.new(method) if closed?
|
153
158
|
end
|
154
159
|
end
|
155
160
|
end
|
data/lib/rdkafka/bindings.rb
CHANGED
@@ -17,7 +17,7 @@ module Rdkafka
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
ffi_lib File.join(
|
20
|
+
ffi_lib File.join(__dir__, "../../ext/librdkafka.#{lib_extension}")
|
21
21
|
|
22
22
|
RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS = -175
|
23
23
|
RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS = -174
|
@@ -113,6 +113,7 @@ module Rdkafka
|
|
113
113
|
attach_function :rd_kafka_conf_set_stats_cb, [:pointer, :stats_cb], :void
|
114
114
|
callback :error_cb, [:pointer, :int, :string, :pointer], :void
|
115
115
|
attach_function :rd_kafka_conf_set_error_cb, [:pointer, :error_cb], :void
|
116
|
+
attach_function :rd_kafka_rebalance_protocol, [:pointer], :string
|
116
117
|
|
117
118
|
# Log queue
|
118
119
|
attach_function :rd_kafka_set_log_queue, [:pointer, :pointer], :void
|
@@ -168,7 +169,9 @@ module Rdkafka
|
|
168
169
|
]
|
169
170
|
|
170
171
|
attach_function :rd_kafka_new, [:kafka_type, :pointer, :pointer, :int], :pointer
|
171
|
-
|
172
|
+
|
173
|
+
RD_KAFKA_DESTROY_F_IMMEDIATE = 0x4
|
174
|
+
attach_function :rd_kafka_destroy_flags, [:pointer, :int], :void
|
172
175
|
|
173
176
|
# Consumer
|
174
177
|
|
@@ -176,6 +179,8 @@ module Rdkafka
|
|
176
179
|
attach_function :rd_kafka_unsubscribe, [:pointer], :int
|
177
180
|
attach_function :rd_kafka_subscription, [:pointer, :pointer], :int
|
178
181
|
attach_function :rd_kafka_assign, [:pointer, :pointer], :int
|
182
|
+
attach_function :rd_kafka_incremental_assign, [:pointer, :pointer], :int
|
183
|
+
attach_function :rd_kafka_incremental_unassign, [:pointer, :pointer], :int
|
179
184
|
attach_function :rd_kafka_assignment, [:pointer, :pointer], :int
|
180
185
|
attach_function :rd_kafka_committed, [:pointer, :pointer, :int], :int
|
181
186
|
attach_function :rd_kafka_commit, [:pointer, :pointer, :bool], :int, blocking: true
|
@@ -201,9 +206,17 @@ module Rdkafka
|
|
201
206
|
) do |client_ptr, code, partitions_ptr, opaque_ptr|
|
202
207
|
case code
|
203
208
|
when RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS
|
204
|
-
Rdkafka::Bindings.
|
209
|
+
if Rdkafka::Bindings.rd_kafka_rebalance_protocol(client_ptr) == "COOPERATIVE"
|
210
|
+
Rdkafka::Bindings.rd_kafka_incremental_assign(client_ptr, partitions_ptr)
|
211
|
+
else
|
212
|
+
Rdkafka::Bindings.rd_kafka_assign(client_ptr, partitions_ptr)
|
213
|
+
end
|
205
214
|
else # RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS or errors
|
206
|
-
Rdkafka::Bindings.
|
215
|
+
if Rdkafka::Bindings.rd_kafka_rebalance_protocol(client_ptr) == "COOPERATIVE"
|
216
|
+
Rdkafka::Bindings.rd_kafka_incremental_unassign(client_ptr, partitions_ptr)
|
217
|
+
else
|
218
|
+
Rdkafka::Bindings.rd_kafka_assign(client_ptr, FFI::Pointer::NULL)
|
219
|
+
end
|
207
220
|
end
|
208
221
|
|
209
222
|
opaque = Rdkafka::Config.opaques[opaque_ptr.to_i]
|
data/lib/rdkafka/config.rb
CHANGED
@@ -157,13 +157,14 @@ module Rdkafka
|
|
157
157
|
Rdkafka::Bindings.rd_kafka_conf_set_rebalance_cb(config, Rdkafka::Bindings::RebalanceCallback)
|
158
158
|
end
|
159
159
|
|
160
|
+
# Create native client
|
160
161
|
kafka = native_kafka(config, :rd_kafka_consumer)
|
161
162
|
|
162
163
|
# Redirect the main queue to the consumer
|
163
164
|
Rdkafka::Bindings.rd_kafka_poll_set_consumer(kafka)
|
164
165
|
|
165
166
|
# Return consumer with Kafka client
|
166
|
-
Rdkafka::Consumer.new(kafka)
|
167
|
+
Rdkafka::Consumer.new(Rdkafka::NativeKafka.new(kafka, run_polling_thread: false))
|
167
168
|
end
|
168
169
|
|
169
170
|
# Create a producer with this configuration.
|
@@ -181,7 +182,7 @@ module Rdkafka
|
|
181
182
|
Rdkafka::Bindings.rd_kafka_conf_set_dr_msg_cb(config, Rdkafka::Callbacks::DeliveryCallbackFunction)
|
182
183
|
# Return producer with Kafka client
|
183
184
|
partitioner_name = self[:partitioner] || self["partitioner"]
|
184
|
-
Rdkafka::Producer.new(Rdkafka::NativeKafka.new(native_kafka(config, :rd_kafka_producer)), partitioner_name).tap do |producer|
|
185
|
+
Rdkafka::Producer.new(Rdkafka::NativeKafka.new(native_kafka(config, :rd_kafka_producer), run_polling_thread: true), partitioner_name).tap do |producer|
|
185
186
|
opaque.producer = producer
|
186
187
|
end
|
187
188
|
end
|
@@ -196,7 +197,7 @@ module Rdkafka
|
|
196
197
|
opaque = Opaque.new
|
197
198
|
config = native_config(opaque)
|
198
199
|
Rdkafka::Bindings.rd_kafka_conf_set_background_event_cb(config, Rdkafka::Callbacks::BackgroundEventCallbackFunction)
|
199
|
-
Rdkafka::Admin.new(Rdkafka::NativeKafka.new(native_kafka(config, :rd_kafka_producer)))
|
200
|
+
Rdkafka::Admin.new(Rdkafka::NativeKafka.new(native_kafka(config, :rd_kafka_producer), run_polling_thread: true))
|
200
201
|
end
|
201
202
|
|
202
203
|
# Error that is returned by the underlying rdkafka error if an invalid configuration option is present.
|
@@ -2,11 +2,25 @@
|
|
2
2
|
|
3
3
|
module Rdkafka
|
4
4
|
class Consumer
|
5
|
-
#
|
6
|
-
|
7
|
-
|
5
|
+
# Interface to return headers for a consumer message
|
6
|
+
module Headers
|
7
|
+
class HashWithSymbolKeysTreatedLikeStrings < Hash
|
8
|
+
def [](key)
|
9
|
+
if key.is_a?(Symbol)
|
10
|
+
Kernel.warn("rdkafka deprecation warning: header access with Symbol key #{key.inspect} treated as a String. " \
|
11
|
+
"Please change your code to use String keys to avoid this warning. Symbol keys will break in version 1.")
|
12
|
+
super(key.to_s)
|
13
|
+
else
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Reads a librdkafka native message's headers and returns them as a Ruby Hash
|
20
|
+
#
|
21
|
+
# @param [librdkakfa message] native_message
|
8
22
|
#
|
9
|
-
# @return [Hash<String, String>]
|
23
|
+
# @return [Hash<String, String>] headers Hash for the native_message
|
10
24
|
#
|
11
25
|
# @raise [Rdkafka::RdkafkaError] when fail to read headers
|
12
26
|
#
|
@@ -26,7 +40,8 @@ module Rdkafka
|
|
26
40
|
name_ptrptr = FFI::MemoryPointer.new(:pointer)
|
27
41
|
value_ptrptr = FFI::MemoryPointer.new(:pointer)
|
28
42
|
size_ptr = Rdkafka::Bindings::SizePtr.new
|
29
|
-
|
43
|
+
|
44
|
+
headers = HashWithSymbolKeysTreatedLikeStrings.new
|
30
45
|
|
31
46
|
idx = 0
|
32
47
|
loop do
|
@@ -53,12 +68,12 @@ module Rdkafka
|
|
53
68
|
|
54
69
|
value = value_ptr.read_string(size)
|
55
70
|
|
56
|
-
headers[name
|
71
|
+
headers[name] = value
|
57
72
|
|
58
73
|
idx += 1
|
59
74
|
end
|
60
75
|
|
61
|
-
headers
|
76
|
+
headers.freeze
|
62
77
|
end
|
63
78
|
end
|
64
79
|
end
|
data/lib/rdkafka/consumer.rb
CHANGED
@@ -26,15 +26,14 @@ module Rdkafka
|
|
26
26
|
# @return [nil]
|
27
27
|
def close
|
28
28
|
return if closed?
|
29
|
-
|
30
|
-
Rdkafka::Bindings.rd_kafka_consumer_close(@native_kafka)
|
31
|
-
|
32
|
-
@native_kafka = nil
|
29
|
+
ObjectSpace.undefine_finalizer(self)
|
30
|
+
Rdkafka::Bindings.rd_kafka_consumer_close(@native_kafka.inner)
|
31
|
+
@native_kafka.close
|
33
32
|
end
|
34
33
|
|
35
34
|
# Whether this consumer has closed
|
36
35
|
def closed?
|
37
|
-
@native_kafka.
|
36
|
+
@native_kafka.closed?
|
38
37
|
end
|
39
38
|
|
40
39
|
# Subscribe to one or more topics letting Kafka handle partition assignments.
|
@@ -55,7 +54,7 @@ module Rdkafka
|
|
55
54
|
end
|
56
55
|
|
57
56
|
# Subscribe to topic partition list and check this was successful
|
58
|
-
response = Rdkafka::Bindings.rd_kafka_subscribe(@native_kafka, tpl)
|
57
|
+
response = Rdkafka::Bindings.rd_kafka_subscribe(@native_kafka.inner, tpl)
|
59
58
|
if response != 0
|
60
59
|
raise Rdkafka::RdkafkaError.new(response, "Error subscribing to '#{topics.join(', ')}'")
|
61
60
|
end
|
@@ -71,7 +70,7 @@ module Rdkafka
|
|
71
70
|
def unsubscribe
|
72
71
|
closed_consumer_check(__method__)
|
73
72
|
|
74
|
-
response = Rdkafka::Bindings.rd_kafka_unsubscribe(@native_kafka)
|
73
|
+
response = Rdkafka::Bindings.rd_kafka_unsubscribe(@native_kafka.inner)
|
75
74
|
if response != 0
|
76
75
|
raise Rdkafka::RdkafkaError.new(response)
|
77
76
|
end
|
@@ -94,7 +93,7 @@ module Rdkafka
|
|
94
93
|
tpl = list.to_native_tpl
|
95
94
|
|
96
95
|
begin
|
97
|
-
response = Rdkafka::Bindings.rd_kafka_pause_partitions(@native_kafka, tpl)
|
96
|
+
response = Rdkafka::Bindings.rd_kafka_pause_partitions(@native_kafka.inner, tpl)
|
98
97
|
|
99
98
|
if response != 0
|
100
99
|
list = TopicPartitionList.from_native_tpl(tpl)
|
@@ -122,7 +121,7 @@ module Rdkafka
|
|
122
121
|
tpl = list.to_native_tpl
|
123
122
|
|
124
123
|
begin
|
125
|
-
response = Rdkafka::Bindings.rd_kafka_resume_partitions(@native_kafka, tpl)
|
124
|
+
response = Rdkafka::Bindings.rd_kafka_resume_partitions(@native_kafka.inner, tpl)
|
126
125
|
if response != 0
|
127
126
|
raise Rdkafka::RdkafkaError.new(response, "Error resume '#{list.to_h}'")
|
128
127
|
end
|
@@ -140,7 +139,7 @@ module Rdkafka
|
|
140
139
|
closed_consumer_check(__method__)
|
141
140
|
|
142
141
|
ptr = FFI::MemoryPointer.new(:pointer)
|
143
|
-
response = Rdkafka::Bindings.rd_kafka_subscription(@native_kafka, ptr)
|
142
|
+
response = Rdkafka::Bindings.rd_kafka_subscription(@native_kafka.inner, ptr)
|
144
143
|
|
145
144
|
if response != 0
|
146
145
|
raise Rdkafka::RdkafkaError.new(response)
|
@@ -170,7 +169,7 @@ module Rdkafka
|
|
170
169
|
tpl = list.to_native_tpl
|
171
170
|
|
172
171
|
begin
|
173
|
-
response = Rdkafka::Bindings.rd_kafka_assign(@native_kafka, tpl)
|
172
|
+
response = Rdkafka::Bindings.rd_kafka_assign(@native_kafka.inner, tpl)
|
174
173
|
if response != 0
|
175
174
|
raise Rdkafka::RdkafkaError.new(response, "Error assigning '#{list.to_h}'")
|
176
175
|
end
|
@@ -188,7 +187,7 @@ module Rdkafka
|
|
188
187
|
closed_consumer_check(__method__)
|
189
188
|
|
190
189
|
ptr = FFI::MemoryPointer.new(:pointer)
|
191
|
-
response = Rdkafka::Bindings.rd_kafka_assignment(@native_kafka, ptr)
|
190
|
+
response = Rdkafka::Bindings.rd_kafka_assignment(@native_kafka.inner, ptr)
|
192
191
|
if response != 0
|
193
192
|
raise Rdkafka::RdkafkaError.new(response)
|
194
193
|
end
|
@@ -227,7 +226,7 @@ module Rdkafka
|
|
227
226
|
tpl = list.to_native_tpl
|
228
227
|
|
229
228
|
begin
|
230
|
-
response = Rdkafka::Bindings.rd_kafka_committed(@native_kafka, tpl, timeout_ms)
|
229
|
+
response = Rdkafka::Bindings.rd_kafka_committed(@native_kafka.inner, tpl, timeout_ms)
|
231
230
|
if response != 0
|
232
231
|
raise Rdkafka::RdkafkaError.new(response)
|
233
232
|
end
|
@@ -253,7 +252,7 @@ module Rdkafka
|
|
253
252
|
high = FFI::MemoryPointer.new(:int64, 1)
|
254
253
|
|
255
254
|
response = Rdkafka::Bindings.rd_kafka_query_watermark_offsets(
|
256
|
-
@native_kafka,
|
255
|
+
@native_kafka.inner,
|
257
256
|
topic,
|
258
257
|
partition,
|
259
258
|
low,
|
@@ -307,7 +306,7 @@ module Rdkafka
|
|
307
306
|
# @return [String, nil]
|
308
307
|
def cluster_id
|
309
308
|
closed_consumer_check(__method__)
|
310
|
-
Rdkafka::Bindings.rd_kafka_clusterid(@native_kafka)
|
309
|
+
Rdkafka::Bindings.rd_kafka_clusterid(@native_kafka.inner)
|
311
310
|
end
|
312
311
|
|
313
312
|
# Returns this client's broker-assigned group member id
|
@@ -317,7 +316,7 @@ module Rdkafka
|
|
317
316
|
# @return [String, nil]
|
318
317
|
def member_id
|
319
318
|
closed_consumer_check(__method__)
|
320
|
-
Rdkafka::Bindings.rd_kafka_memberid(@native_kafka)
|
319
|
+
Rdkafka::Bindings.rd_kafka_memberid(@native_kafka.inner)
|
321
320
|
end
|
322
321
|
|
323
322
|
# Store offset of a message to be used in the next commit of this consumer
|
@@ -335,7 +334,7 @@ module Rdkafka
|
|
335
334
|
# rd_kafka_offset_store is one of the few calls that does not support
|
336
335
|
# a string as the topic, so create a native topic for it.
|
337
336
|
native_topic = Rdkafka::Bindings.rd_kafka_topic_new(
|
338
|
-
@native_kafka,
|
337
|
+
@native_kafka.inner,
|
339
338
|
message.topic,
|
340
339
|
nil
|
341
340
|
)
|
@@ -367,7 +366,7 @@ module Rdkafka
|
|
367
366
|
# rd_kafka_offset_store is one of the few calls that does not support
|
368
367
|
# a string as the topic, so create a native topic for it.
|
369
368
|
native_topic = Rdkafka::Bindings.rd_kafka_topic_new(
|
370
|
-
@native_kafka,
|
369
|
+
@native_kafka.inner,
|
371
370
|
message.topic,
|
372
371
|
nil
|
373
372
|
)
|
@@ -411,7 +410,7 @@ module Rdkafka
|
|
411
410
|
tpl = list ? list.to_native_tpl : nil
|
412
411
|
|
413
412
|
begin
|
414
|
-
response = Rdkafka::Bindings.rd_kafka_commit(@native_kafka, tpl, async)
|
413
|
+
response = Rdkafka::Bindings.rd_kafka_commit(@native_kafka.inner, tpl, async)
|
415
414
|
if response != 0
|
416
415
|
raise Rdkafka::RdkafkaError.new(response)
|
417
416
|
end
|
@@ -430,7 +429,7 @@ module Rdkafka
|
|
430
429
|
def poll(timeout_ms)
|
431
430
|
closed_consumer_check(__method__)
|
432
431
|
|
433
|
-
message_ptr = Rdkafka::Bindings.rd_kafka_consumer_poll(@native_kafka, timeout_ms)
|
432
|
+
message_ptr = Rdkafka::Bindings.rd_kafka_consumer_poll(@native_kafka.inner, timeout_ms)
|
434
433
|
if message_ptr.null?
|
435
434
|
nil
|
436
435
|
else
|
@@ -445,7 +444,7 @@ module Rdkafka
|
|
445
444
|
end
|
446
445
|
ensure
|
447
446
|
# Clean up rdkafka message if there is one
|
448
|
-
if
|
447
|
+
if message_ptr && !message_ptr.null?
|
449
448
|
Rdkafka::Bindings.rd_kafka_message_destroy(message_ptr)
|
450
449
|
end
|
451
450
|
end
|
@@ -550,7 +549,7 @@ module Rdkafka
|
|
550
549
|
end
|
551
550
|
if message
|
552
551
|
slice << message
|
553
|
-
bytes += message.payload.bytesize
|
552
|
+
bytes += message.payload.bytesize if message.payload
|
554
553
|
end
|
555
554
|
if slice.size == max_items || bytes >= bytes_threshold || monotonic_now >= end_time - 0.001
|
556
555
|
yield slice.dup, nil
|
data/lib/rdkafka/native_kafka.rb
CHANGED
@@ -4,21 +4,26 @@ module Rdkafka
|
|
4
4
|
# @private
|
5
5
|
# A wrapper around a native kafka that polls and cleanly exits
|
6
6
|
class NativeKafka
|
7
|
-
def initialize(inner)
|
7
|
+
def initialize(inner, run_polling_thread:)
|
8
8
|
@inner = inner
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
if run_polling_thread
|
11
|
+
# Start thread to poll client for delivery callbacks,
|
12
|
+
# not used in consumer.
|
13
|
+
@polling_thread = Thread.new do
|
14
|
+
loop do
|
15
|
+
Rdkafka::Bindings.rd_kafka_poll(inner, 250)
|
16
|
+
# Exit thread if closing and the poll queue is empty
|
17
|
+
if Thread.current[:closing] && Rdkafka::Bindings.rd_kafka_outq_len(inner) == 0
|
18
|
+
break
|
19
|
+
end
|
17
20
|
end
|
18
21
|
end
|
22
|
+
@polling_thread.abort_on_exception = true
|
23
|
+
@polling_thread[:closing] = false
|
19
24
|
end
|
20
|
-
|
21
|
-
@
|
25
|
+
|
26
|
+
@closing = false
|
22
27
|
end
|
23
28
|
|
24
29
|
def inner
|
@@ -30,22 +35,27 @@ module Rdkafka
|
|
30
35
|
end
|
31
36
|
|
32
37
|
def closed?
|
33
|
-
@inner.nil?
|
38
|
+
@closing || @inner.nil?
|
34
39
|
end
|
35
40
|
|
36
41
|
def close(object_id=nil)
|
37
42
|
return if closed?
|
38
43
|
|
39
|
-
#
|
40
|
-
|
41
|
-
|
42
|
-
# Indicate to polling thread that we're closing
|
43
|
-
@polling_thread[:closing] = true
|
44
|
-
# Wait for the polling thread to finish up
|
45
|
-
@polling_thread.join
|
44
|
+
# Indicate to the outside world that we are closing
|
45
|
+
@closing = true
|
46
46
|
|
47
|
-
|
47
|
+
if @polling_thread
|
48
|
+
# Indicate to polling thread that we're closing
|
49
|
+
@polling_thread[:closing] = true
|
50
|
+
# Wait for the polling thread to finish up
|
51
|
+
@polling_thread.join
|
52
|
+
end
|
48
53
|
|
54
|
+
# Destroy the client
|
55
|
+
Rdkafka::Bindings.rd_kafka_destroy_flags(
|
56
|
+
@inner,
|
57
|
+
Rdkafka::Bindings::RD_KAFKA_DESTROY_F_IMMEDIATE
|
58
|
+
)
|
49
59
|
@inner = nil
|
50
60
|
end
|
51
61
|
end
|
data/lib/rdkafka/producer.rb
CHANGED
@@ -40,11 +40,23 @@ module Rdkafka
|
|
40
40
|
|
41
41
|
# Close this producer and wait for the internal poll queue to empty.
|
42
42
|
def close
|
43
|
+
return if closed?
|
43
44
|
ObjectSpace.undefine_finalizer(self)
|
44
|
-
|
45
45
|
@native_kafka.close
|
46
46
|
end
|
47
47
|
|
48
|
+
# Whether this producer has closed
|
49
|
+
def closed?
|
50
|
+
@native_kafka.closed?
|
51
|
+
end
|
52
|
+
|
53
|
+
# Wait until all outstanding producer requests are completed, with the given timeout
|
54
|
+
# in seconds. Call this before closing a producer to ensure delivery of all messages.
|
55
|
+
def flush(timeout_ms=5_000)
|
56
|
+
closed_producer_check(__method__)
|
57
|
+
Rdkafka::Bindings.rd_kafka_flush(@native_kafka.inner, timeout_ms)
|
58
|
+
end
|
59
|
+
|
48
60
|
# Partition count for a given topic.
|
49
61
|
# NOTE: If 'allow.auto.create.topics' is set to true in the broker, the topic will be auto-created after returning nil.
|
50
62
|
#
|
@@ -173,7 +185,7 @@ module Rdkafka
|
|
173
185
|
|
174
186
|
private
|
175
187
|
def closed_producer_check(method)
|
176
|
-
raise Rdkafka::ClosedProducerError.new(method) if
|
188
|
+
raise Rdkafka::ClosedProducerError.new(method) if closed?
|
177
189
|
end
|
178
190
|
end
|
179
191
|
end
|
data/lib/rdkafka/version.rb
CHANGED
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe Rdkafka::Consumer::Headers do
|
6
|
+
let(:headers) do
|
7
|
+
{ # Note String keys!
|
8
|
+
"version" => "2.1.3",
|
9
|
+
"type" => "String"
|
10
|
+
}
|
11
|
+
end
|
12
|
+
let(:native_message) { double('native message') }
|
13
|
+
let(:headers_ptr) { double('headers pointer') }
|
14
|
+
|
15
|
+
describe '.from_native' do
|
16
|
+
before do
|
17
|
+
expect(Rdkafka::Bindings).to receive(:rd_kafka_message_headers).with(native_message, anything) do |_, headers_ptrptr|
|
18
|
+
expect(headers_ptrptr).to receive(:read_pointer).and_return(headers_ptr)
|
19
|
+
Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
20
|
+
end
|
21
|
+
|
22
|
+
expect(Rdkafka::Bindings).to \
|
23
|
+
receive(:rd_kafka_header_get_all)
|
24
|
+
.with(headers_ptr, 0, anything, anything, anything) do |_, _, name_ptrptr, value_ptrptr, size_ptr|
|
25
|
+
expect(name_ptrptr).to receive(:read_pointer).and_return(double("pointer 0", read_string_to_null: headers.keys[0]))
|
26
|
+
expect(size_ptr).to receive(:[]).with(:value).and_return(headers.keys[0].size)
|
27
|
+
expect(value_ptrptr).to receive(:read_pointer).and_return(double("value pointer 0", read_string: headers.values[0]))
|
28
|
+
Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
29
|
+
end
|
30
|
+
|
31
|
+
expect(Rdkafka::Bindings).to \
|
32
|
+
receive(:rd_kafka_header_get_all)
|
33
|
+
.with(headers_ptr, 1, anything, anything, anything) do |_, _, name_ptrptr, value_ptrptr, size_ptr|
|
34
|
+
expect(name_ptrptr).to receive(:read_pointer).and_return(double("pointer 1", read_string_to_null: headers.keys[1]))
|
35
|
+
expect(size_ptr).to receive(:[]).with(:value).and_return(headers.keys[1].size)
|
36
|
+
expect(value_ptrptr).to receive(:read_pointer).and_return(double("value pointer 1", read_string: headers.values[1]))
|
37
|
+
Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
38
|
+
end
|
39
|
+
|
40
|
+
expect(Rdkafka::Bindings).to \
|
41
|
+
receive(:rd_kafka_header_get_all)
|
42
|
+
.with(headers_ptr, 2, anything, anything, anything)
|
43
|
+
.and_return(Rdkafka::Bindings::RD_KAFKA_RESP_ERR__NOENT)
|
44
|
+
end
|
45
|
+
|
46
|
+
subject { described_class.from_native(native_message) }
|
47
|
+
|
48
|
+
it { is_expected.to eq(headers) }
|
49
|
+
it { is_expected.to be_frozen }
|
50
|
+
|
51
|
+
it 'allows String key' do
|
52
|
+
expect(subject['version']).to eq("2.1.3")
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'allows Symbol key, but warns' do
|
56
|
+
expect(Kernel).to \
|
57
|
+
receive(:warn).with("rdkafka deprecation warning: header access with Symbol key :version treated as a String. " \
|
58
|
+
"Please change your code to use String keys to avoid this warning. Symbol keys will break in version 1.")
|
59
|
+
expect(subject[:version]).to eq("2.1.3")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -28,7 +28,7 @@ describe Rdkafka::Consumer::Message do
|
|
28
28
|
end
|
29
29
|
|
30
30
|
after(:each) do
|
31
|
-
Rdkafka::Bindings.
|
31
|
+
Rdkafka::Bindings.rd_kafka_destroy_flags(native_client, Rdkafka::Bindings::RD_KAFKA_DESTROY_F_IMMEDIATE)
|
32
32
|
end
|
33
33
|
|
34
34
|
subject { Rdkafka::Consumer::Message.new(native_message) }
|
@@ -595,7 +595,7 @@ describe Rdkafka::Consumer do
|
|
595
595
|
end
|
596
596
|
|
597
597
|
describe "#poll with headers" do
|
598
|
-
it "should return message with headers" do
|
598
|
+
it "should return message with headers using string keys (when produced with symbol keys)" do
|
599
599
|
report = producer.produce(
|
600
600
|
topic: "consume_test_topic",
|
601
601
|
key: "key headers",
|
@@ -605,7 +605,20 @@ describe Rdkafka::Consumer do
|
|
605
605
|
message = wait_for_message(topic: "consume_test_topic", consumer: consumer, delivery_report: report)
|
606
606
|
expect(message).to be
|
607
607
|
expect(message.key).to eq('key headers')
|
608
|
-
expect(message.headers).to include(foo
|
608
|
+
expect(message.headers).to include('foo' => 'bar')
|
609
|
+
end
|
610
|
+
|
611
|
+
it "should return message with headers using string keys (when produced with string keys)" do
|
612
|
+
report = producer.produce(
|
613
|
+
topic: "consume_test_topic",
|
614
|
+
key: "key headers",
|
615
|
+
headers: { 'foo' => 'bar' }
|
616
|
+
).wait
|
617
|
+
|
618
|
+
message = wait_for_message(topic: "consume_test_topic", consumer: consumer, delivery_report: report)
|
619
|
+
expect(message).to be
|
620
|
+
expect(message.key).to eq('key headers')
|
621
|
+
expect(message.headers).to include('foo' => 'bar')
|
609
622
|
end
|
610
623
|
|
611
624
|
it "should return message with no headers" do
|
@@ -700,7 +713,7 @@ describe Rdkafka::Consumer do
|
|
700
713
|
n.times do |i|
|
701
714
|
handles << producer.produce(
|
702
715
|
topic: topic_name,
|
703
|
-
payload: Time.new.to_f.to_s,
|
716
|
+
payload: i % 10 == 0 ? nil : Time.new.to_f.to_s,
|
704
717
|
key: i.to_s,
|
705
718
|
partition: 0
|
706
719
|
)
|
@@ -964,18 +977,6 @@ describe Rdkafka::Consumer do
|
|
964
977
|
expect(listener.queue).to eq([:assigned, :revoked])
|
965
978
|
end
|
966
979
|
end
|
967
|
-
|
968
|
-
def notify_listener(listener)
|
969
|
-
# 1. subscribe and poll
|
970
|
-
consumer.subscribe("consume_test_topic")
|
971
|
-
wait_for_assignment(consumer)
|
972
|
-
consumer.poll(100)
|
973
|
-
|
974
|
-
# 2. unsubscribe
|
975
|
-
consumer.unsubscribe
|
976
|
-
wait_for_unassignment(consumer)
|
977
|
-
consumer.close
|
978
|
-
end
|
979
980
|
end
|
980
981
|
|
981
982
|
context "methods that should not be called after a consumer has been closed" do
|
@@ -1015,4 +1016,62 @@ describe Rdkafka::Consumer do
|
|
1015
1016
|
|
1016
1017
|
expect(consumer.closed?).to eq(true)
|
1017
1018
|
end
|
1019
|
+
|
1020
|
+
context "when the rebalance protocol is cooperative" do
|
1021
|
+
let(:consumer) do
|
1022
|
+
config = rdkafka_consumer_config(
|
1023
|
+
{
|
1024
|
+
:"partition.assignment.strategy" => "cooperative-sticky",
|
1025
|
+
:"debug" => "consumer",
|
1026
|
+
}
|
1027
|
+
)
|
1028
|
+
config.consumer_rebalance_listener = listener
|
1029
|
+
config.consumer
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
let(:listener) do
|
1033
|
+
Struct.new(:queue) do
|
1034
|
+
def on_partitions_assigned(consumer, list)
|
1035
|
+
collect(:assign, list)
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
def on_partitions_revoked(consumer, list)
|
1039
|
+
collect(:revoke, list)
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
def collect(name, list)
|
1043
|
+
partitions = list.to_h.map { |key, values| [key, values.map(&:partition)] }.flatten
|
1044
|
+
queue << ([name] + partitions)
|
1045
|
+
end
|
1046
|
+
end.new([])
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
it "should be able to assign and unassign partitions using the cooperative partition assignment APIs" do
|
1050
|
+
notify_listener(listener) do
|
1051
|
+
handles = []
|
1052
|
+
10.times do
|
1053
|
+
handles << producer.produce(
|
1054
|
+
topic: "consume_test_topic",
|
1055
|
+
payload: "payload 1",
|
1056
|
+
key: "key 1",
|
1057
|
+
partition: 0
|
1058
|
+
)
|
1059
|
+
end
|
1060
|
+
handles.each(&:wait)
|
1061
|
+
|
1062
|
+
consumer.subscribe("consume_test_topic")
|
1063
|
+
# Check the first 10 messages. Then close the consumer, which
|
1064
|
+
# should break the each loop.
|
1065
|
+
consumer.each_with_index do |message, i|
|
1066
|
+
expect(message).to be_a Rdkafka::Consumer::Message
|
1067
|
+
break if i == 10
|
1068
|
+
end
|
1069
|
+
end
|
1070
|
+
|
1071
|
+
expect(listener.queue).to eq([
|
1072
|
+
[:assign, "consume_test_topic", 0, 1, 2],
|
1073
|
+
[:revoke, "consume_test_topic", 0, 1, 2]
|
1074
|
+
])
|
1075
|
+
end
|
1076
|
+
end
|
1018
1077
|
end
|
@@ -10,7 +10,7 @@ describe Rdkafka::Metadata do
|
|
10
10
|
|
11
11
|
after do
|
12
12
|
Rdkafka::Bindings.rd_kafka_consumer_close(native_kafka)
|
13
|
-
Rdkafka::Bindings.
|
13
|
+
Rdkafka::Bindings.rd_kafka_destroy_flags(native_kafka, Rdkafka::Bindings::RD_KAFKA_DESTROY_F_IMMEDIATE)
|
14
14
|
end
|
15
15
|
|
16
16
|
context "passing in a topic name" do
|
@@ -8,12 +8,12 @@ describe Rdkafka::NativeKafka do
|
|
8
8
|
let(:closing) { false }
|
9
9
|
let(:thread) { double(Thread) }
|
10
10
|
|
11
|
-
subject(:client) { described_class.new(native) }
|
11
|
+
subject(:client) { described_class.new(native, run_polling_thread: true) }
|
12
12
|
|
13
13
|
before do
|
14
14
|
allow(Rdkafka::Bindings).to receive(:rd_kafka_poll).with(instance_of(FFI::Pointer), 250).and_call_original
|
15
15
|
allow(Rdkafka::Bindings).to receive(:rd_kafka_outq_len).with(instance_of(FFI::Pointer)).and_return(0).and_call_original
|
16
|
-
allow(Rdkafka::Bindings).to receive(:
|
16
|
+
allow(Rdkafka::Bindings).to receive(:rd_kafka_destroy_flags)
|
17
17
|
allow(Thread).to receive(:new).and_return(thread)
|
18
18
|
|
19
19
|
allow(thread).to receive(:[]=).with(:closing, anything)
|
@@ -53,6 +53,16 @@ describe Rdkafka::NativeKafka do
|
|
53
53
|
expect(Rdkafka::Bindings).to receive(:rd_kafka_outq_len).with(native).at_least(:once)
|
54
54
|
end
|
55
55
|
end
|
56
|
+
|
57
|
+
context "if not enabled" do
|
58
|
+
subject(:client) { described_class.new(native, run_polling_thread: false) }
|
59
|
+
|
60
|
+
it "is not created" do
|
61
|
+
expect(Thread).not_to receive(:new)
|
62
|
+
|
63
|
+
client
|
64
|
+
end
|
65
|
+
end
|
56
66
|
end
|
57
67
|
|
58
68
|
def polling_loop_expects(&block)
|
@@ -76,7 +86,7 @@ describe Rdkafka::NativeKafka do
|
|
76
86
|
|
77
87
|
context "and attempt to close" do
|
78
88
|
it "calls the `destroy` binding" do
|
79
|
-
expect(Rdkafka::Bindings).to receive(:
|
89
|
+
expect(Rdkafka::Bindings).to receive(:rd_kafka_destroy_flags).with(native, Rdkafka::Bindings::RD_KAFKA_DESTROY_F_IMMEDIATE)
|
80
90
|
|
81
91
|
client.close
|
82
92
|
end
|
@@ -111,7 +121,7 @@ describe Rdkafka::NativeKafka do
|
|
111
121
|
|
112
122
|
context "and attempt to close again" do
|
113
123
|
it "does not call the `destroy` binding" do
|
114
|
-
expect(Rdkafka::Bindings).not_to receive(:
|
124
|
+
expect(Rdkafka::Bindings).not_to receive(:rd_kafka_destroy_flags)
|
115
125
|
|
116
126
|
client.close
|
117
127
|
end
|
@@ -9,7 +9,8 @@ describe Rdkafka::Producer do
|
|
9
9
|
|
10
10
|
after do
|
11
11
|
# Registry should always end up being empty
|
12
|
-
|
12
|
+
registry = Rdkafka::Producer::DeliveryHandle::REGISTRY
|
13
|
+
expect(registry).to be_empty, registry.inspect
|
13
14
|
producer.close
|
14
15
|
consumer.close
|
15
16
|
end
|
@@ -184,10 +185,11 @@ describe Rdkafka::Producer do
|
|
184
185
|
expect(report.partition).to eq 1
|
185
186
|
expect(report.offset).to be >= 0
|
186
187
|
|
187
|
-
#
|
188
|
+
# Flush and close producer
|
189
|
+
producer.flush
|
188
190
|
producer.close
|
189
191
|
|
190
|
-
# Consume message and verify
|
192
|
+
# Consume message and verify its content
|
191
193
|
message = wait_for_message(
|
192
194
|
topic: "produce_test_topic",
|
193
195
|
delivery_report: report,
|
@@ -211,7 +213,7 @@ describe Rdkafka::Producer do
|
|
211
213
|
)
|
212
214
|
report = handle.wait(max_wait_timeout: 5)
|
213
215
|
|
214
|
-
# Consume message and verify
|
216
|
+
# Consume message and verify its content
|
215
217
|
message = wait_for_message(
|
216
218
|
topic: "produce_test_topic",
|
217
219
|
delivery_report: report,
|
@@ -285,7 +287,7 @@ describe Rdkafka::Producer do
|
|
285
287
|
)
|
286
288
|
report = handle.wait(max_wait_timeout: 5)
|
287
289
|
|
288
|
-
# Consume message and verify
|
290
|
+
# Consume message and verify its content
|
289
291
|
message = wait_for_message(
|
290
292
|
topic: "produce_test_topic",
|
291
293
|
delivery_report: report,
|
@@ -318,7 +320,7 @@ describe Rdkafka::Producer do
|
|
318
320
|
)
|
319
321
|
report = handle.wait(max_wait_timeout: 5)
|
320
322
|
|
321
|
-
# Consume message and verify
|
323
|
+
# Consume message and verify its content
|
322
324
|
message = wait_for_message(
|
323
325
|
topic: "produce_test_topic",
|
324
326
|
delivery_report: report,
|
@@ -339,7 +341,7 @@ describe Rdkafka::Producer do
|
|
339
341
|
)
|
340
342
|
report = handle.wait(max_wait_timeout: 5)
|
341
343
|
|
342
|
-
# Consume message and verify
|
344
|
+
# Consume message and verify its content
|
343
345
|
message = wait_for_message(
|
344
346
|
topic: "produce_test_topic",
|
345
347
|
delivery_report: report,
|
@@ -359,7 +361,7 @@ describe Rdkafka::Producer do
|
|
359
361
|
)
|
360
362
|
report = handle.wait(max_wait_timeout: 5)
|
361
363
|
|
362
|
-
# Consume message and verify
|
364
|
+
# Consume message and verify its content
|
363
365
|
message = wait_for_message(
|
364
366
|
topic: "produce_test_topic",
|
365
367
|
delivery_report: report,
|
@@ -377,7 +379,7 @@ describe Rdkafka::Producer do
|
|
377
379
|
)
|
378
380
|
report = handle.wait(max_wait_timeout: 5)
|
379
381
|
|
380
|
-
# Consume message and verify
|
382
|
+
# Consume message and verify its content
|
381
383
|
message = wait_for_message(
|
382
384
|
topic: "produce_test_topic",
|
383
385
|
delivery_report: report,
|
@@ -397,7 +399,7 @@ describe Rdkafka::Producer do
|
|
397
399
|
)
|
398
400
|
report = handle.wait(max_wait_timeout: 5)
|
399
401
|
|
400
|
-
# Consume message and verify
|
402
|
+
# Consume message and verify its content
|
401
403
|
message = wait_for_message(
|
402
404
|
topic: "produce_test_topic",
|
403
405
|
delivery_report: report,
|
@@ -406,9 +408,9 @@ describe Rdkafka::Producer do
|
|
406
408
|
|
407
409
|
expect(message.payload).to eq "payload headers"
|
408
410
|
expect(message.key).to eq "key headers"
|
409
|
-
expect(message.headers[
|
410
|
-
expect(message.headers[
|
411
|
-
expect(message.headers[
|
411
|
+
expect(message.headers["foo"]).to eq "bar"
|
412
|
+
expect(message.headers["baz"]).to eq "foobar"
|
413
|
+
expect(message.headers["foobar"]).to be_nil
|
412
414
|
end
|
413
415
|
|
414
416
|
it "should produce a message with empty headers" do
|
@@ -420,7 +422,7 @@ describe Rdkafka::Producer do
|
|
420
422
|
)
|
421
423
|
report = handle.wait(max_wait_timeout: 5)
|
422
424
|
|
423
|
-
# Consume message and verify
|
425
|
+
# Consume message and verify its content
|
424
426
|
message = wait_for_message(
|
425
427
|
topic: "produce_test_topic",
|
426
428
|
delivery_report: report,
|
@@ -458,10 +460,10 @@ describe Rdkafka::Producer do
|
|
458
460
|
# wait for and check the message in the main process.
|
459
461
|
reader, writer = IO.pipe
|
460
462
|
|
461
|
-
fork do
|
463
|
+
pid = fork do
|
462
464
|
reader.close
|
463
465
|
|
464
|
-
#
|
466
|
+
# Avoid sharing the client between processes.
|
465
467
|
producer = rdkafka_producer_config.producer
|
466
468
|
|
467
469
|
handle = producer.produce(
|
@@ -480,8 +482,10 @@ describe Rdkafka::Producer do
|
|
480
482
|
|
481
483
|
writer.write(report_json)
|
482
484
|
writer.close
|
485
|
+
producer.flush
|
483
486
|
producer.close
|
484
487
|
end
|
488
|
+
Process.wait(pid)
|
485
489
|
|
486
490
|
writer.close
|
487
491
|
report_hash = JSON.parse(reader.read)
|
@@ -493,7 +497,7 @@ describe Rdkafka::Producer do
|
|
493
497
|
|
494
498
|
reader.close
|
495
499
|
|
496
|
-
# Consume message and verify
|
500
|
+
# Consume message and verify its content
|
497
501
|
message = wait_for_message(
|
498
502
|
topic: "produce_test_topic",
|
499
503
|
delivery_report: report,
|
data/spec/spec_helper.rb
CHANGED
@@ -106,6 +106,20 @@ def wait_for_unassignment(consumer)
|
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
109
|
+
def notify_listener(listener, &block)
|
110
|
+
# 1. subscribe and poll
|
111
|
+
consumer.subscribe("consume_test_topic")
|
112
|
+
wait_for_assignment(consumer)
|
113
|
+
consumer.poll(100)
|
114
|
+
|
115
|
+
block.call if block
|
116
|
+
|
117
|
+
# 2. unsubscribe
|
118
|
+
consumer.unsubscribe
|
119
|
+
wait_for_unassignment(consumer)
|
120
|
+
consumer.close
|
121
|
+
end
|
122
|
+
|
109
123
|
RSpec.configure do |config|
|
110
124
|
config.filter_run focus: true
|
111
125
|
config.run_all_when_everything_filtered = true
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rdkafka
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.13.0.beta.
|
4
|
+
version: 0.13.0.beta.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thijs Cadier
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-10-
|
11
|
+
date: 2022-10-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -191,6 +191,7 @@ files:
|
|
191
191
|
- spec/rdkafka/bindings_spec.rb
|
192
192
|
- spec/rdkafka/callbacks_spec.rb
|
193
193
|
- spec/rdkafka/config_spec.rb
|
194
|
+
- spec/rdkafka/consumer/headers_spec.rb
|
194
195
|
- spec/rdkafka/consumer/message_spec.rb
|
195
196
|
- spec/rdkafka/consumer/partition_spec.rb
|
196
197
|
- spec/rdkafka/consumer/topic_partition_list_spec.rb
|
@@ -221,7 +222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
221
222
|
- !ruby/object:Gem::Version
|
222
223
|
version: 1.3.1
|
223
224
|
requirements: []
|
224
|
-
rubygems_version: 3.3.
|
225
|
+
rubygems_version: 3.3.13
|
225
226
|
signing_key:
|
226
227
|
specification_version: 4
|
227
228
|
summary: The rdkafka gem is a modern Kafka client library for Ruby based on librdkafka.
|
@@ -237,6 +238,7 @@ test_files:
|
|
237
238
|
- spec/rdkafka/bindings_spec.rb
|
238
239
|
- spec/rdkafka/callbacks_spec.rb
|
239
240
|
- spec/rdkafka/config_spec.rb
|
241
|
+
- spec/rdkafka/consumer/headers_spec.rb
|
240
242
|
- spec/rdkafka/consumer/message_spec.rb
|
241
243
|
- spec/rdkafka/consumer/partition_spec.rb
|
242
244
|
- spec/rdkafka/consumer/topic_partition_list_spec.rb
|