rdkafka 0.13.0.beta.2 → 0.13.0.beta.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/.semaphore/semaphore.yml +1 -1
- data/CHANGELOG.md +4 -0
- data/README.md +26 -0
- data/lib/rdkafka/admin.rb +37 -20
- data/lib/rdkafka/bindings.rb +31 -30
- data/lib/rdkafka/config.rb +4 -3
- data/lib/rdkafka/consumer.rb +67 -36
- data/lib/rdkafka/native_kafka.rb +51 -20
- data/lib/rdkafka/producer.rb +28 -7
- data/lib/rdkafka/version.rb +1 -1
- data/spec/rdkafka/config_spec.rb +7 -6
- data/spec/rdkafka/consumer_spec.rb +9 -3
- data/spec/rdkafka/native_kafka_spec.rb +9 -32
- data/spec/rdkafka/producer_spec.rb +6 -3
- data/spec/spec_helper.rb +1 -1
- metadata +4 -6
- data/bin/console +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b5ba8c133537fc31e3fb5f00d230816b92d8c8f63c8d94b39f80d5d7fb22918
|
4
|
+
data.tar.gz: df7679de1c6724585ac5c0e636b6a239ae9d6e859496a855b2596c45e07099ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d1b685394205c9beaf388e40165c028dda9ef3a48ed430e9ccb24e320facadf462215b36e82a62f82cfaf61b1d6040936c352db9bb21371cb4f855d977f9c70
|
7
|
+
data.tar.gz: 65822e65cf3ac6297fec721756cba360f68d3533e69d76ec7208d563d39f0c659a5ac34ef21f633ad0053b9db0cc1cba887d9b4ad75b1083f2ceaf564258c722
|
data/.semaphore/semaphore.yml
CHANGED
@@ -20,8 +20,8 @@ blocks:
|
|
20
20
|
commands:
|
21
21
|
- sem-version ruby $RUBY_VERSION
|
22
22
|
- checkout
|
23
|
+
- docker-compose up -d --no-recreate
|
23
24
|
- bundle install --path vendor/bundle
|
24
25
|
- cd ext && bundle exec rake && cd ..
|
25
|
-
- docker-compose up -d --no-recreate
|
26
26
|
- ulimit -c unlimited
|
27
27
|
- valgrind -v bundle exec rspec
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,10 @@
|
|
7
7
|
* Fix documented type for DeliveryReport#error (jimmydo)
|
8
8
|
* Bump librdkafka to 1.9.2 (thijsc)
|
9
9
|
* Use finalizers to cleanly exit producer and admin (thijsc)
|
10
|
+
* Lock access to the native kafka client (thijsc)
|
11
|
+
- Fix potential race condition in multi-threaded producer (mensfeld)
|
12
|
+
- Fix leaking FFI resources in specs (mensfeld)
|
13
|
+
- Improve specs stability (mensfeld)
|
10
14
|
|
11
15
|
# 0.12.0
|
12
16
|
* Bumps librdkafka to 1.9.0
|
data/README.md
CHANGED
@@ -23,6 +23,19 @@ The most important pieces of a Kafka client are implemented. We're
|
|
23
23
|
working towards feature completeness, you can track that here:
|
24
24
|
https://github.com/appsignal/rdkafka-ruby/milestone/1
|
25
25
|
|
26
|
+
## Table of content
|
27
|
+
|
28
|
+
- [Installation](#installation)
|
29
|
+
- [Usage](#usage)
|
30
|
+
* [Consuming messages](#consuming-messages)
|
31
|
+
* [Producing messages](#producing-messages)
|
32
|
+
- [Higher level libraries](#higher-level-libraries)
|
33
|
+
* [Message processing frameworks](#message-processing-frameworks)
|
34
|
+
* [Message publishing libraries](#message-publishing-libraries)
|
35
|
+
- [Development](#development)
|
36
|
+
- [Example](#example)
|
37
|
+
|
38
|
+
|
26
39
|
## Installation
|
27
40
|
|
28
41
|
This gem downloads and compiles librdkafka when it is installed. If you
|
@@ -77,6 +90,19 @@ Note that creating a producer consumes some resources that will not be
|
|
77
90
|
released until it `#close` is explicitly called, so be sure to call
|
78
91
|
`Config#producer` only as necessary.
|
79
92
|
|
93
|
+
## Higher level libraries
|
94
|
+
|
95
|
+
Currently, there are two actively developed frameworks based on rdkafka-ruby, that provide higher level API that can be used to work with Kafka messages and one library for publishing messages.
|
96
|
+
|
97
|
+
### Message processing frameworks
|
98
|
+
|
99
|
+
* [Karafka](https://github.com/karafka/karafka) - Ruby and Rails efficient Kafka processing framework.
|
100
|
+
* [Racecar](https://github.com/zendesk/racecar) - A simple framework for Kafka consumers in Ruby
|
101
|
+
|
102
|
+
### Message publishing libraries
|
103
|
+
|
104
|
+
* [WaterDrop](https://github.com/karafka/waterdrop) – Standalone Karafka library for producing Kafka messages.
|
105
|
+
|
80
106
|
## Development
|
81
107
|
|
82
108
|
A Docker Compose file is included to run Kafka and Zookeeper. To run
|
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
|
@@ -62,7 +67,9 @@ module Rdkafka
|
|
62
67
|
topics_array_ptr.write_array_of_pointer(pointer_array)
|
63
68
|
|
64
69
|
# Get a pointer to the queue that our request will be enqueued on
|
65
|
-
queue_ptr =
|
70
|
+
queue_ptr = @native_kafka.with_inner do |inner|
|
71
|
+
Rdkafka::Bindings.rd_kafka_queue_get_background(inner)
|
72
|
+
end
|
66
73
|
if queue_ptr.null?
|
67
74
|
Rdkafka::Bindings.rd_kafka_NewTopic_destroy(new_topic_ptr)
|
68
75
|
raise Rdkafka::Config::ConfigError.new("rd_kafka_queue_get_background was NULL")
|
@@ -73,17 +80,21 @@ module Rdkafka
|
|
73
80
|
create_topic_handle[:pending] = true
|
74
81
|
create_topic_handle[:response] = -1
|
75
82
|
CreateTopicHandle.register(create_topic_handle)
|
76
|
-
admin_options_ptr =
|
83
|
+
admin_options_ptr = @native_kafka.with_inner do |inner|
|
84
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_new(inner, Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_CREATETOPICS)
|
85
|
+
end
|
77
86
|
Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, create_topic_handle.to_ptr)
|
78
87
|
|
79
88
|
begin
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
89
|
+
@native_kafka.with_inner do |inner|
|
90
|
+
Rdkafka::Bindings.rd_kafka_CreateTopics(
|
91
|
+
inner,
|
92
|
+
topics_array_ptr,
|
93
|
+
1,
|
94
|
+
admin_options_ptr,
|
95
|
+
queue_ptr
|
96
|
+
)
|
97
|
+
end
|
87
98
|
rescue Exception
|
88
99
|
CreateTopicHandle.remove(create_topic_handle.to_ptr.address)
|
89
100
|
raise
|
@@ -113,7 +124,9 @@ module Rdkafka
|
|
113
124
|
topics_array_ptr.write_array_of_pointer(pointer_array)
|
114
125
|
|
115
126
|
# Get a pointer to the queue that our request will be enqueued on
|
116
|
-
queue_ptr =
|
127
|
+
queue_ptr = @native_kafka.with_inner do |inner|
|
128
|
+
Rdkafka::Bindings.rd_kafka_queue_get_background(inner)
|
129
|
+
end
|
117
130
|
if queue_ptr.null?
|
118
131
|
Rdkafka::Bindings.rd_kafka_DeleteTopic_destroy(delete_topic_ptr)
|
119
132
|
raise Rdkafka::Config::ConfigError.new("rd_kafka_queue_get_background was NULL")
|
@@ -124,17 +137,21 @@ module Rdkafka
|
|
124
137
|
delete_topic_handle[:pending] = true
|
125
138
|
delete_topic_handle[:response] = -1
|
126
139
|
DeleteTopicHandle.register(delete_topic_handle)
|
127
|
-
admin_options_ptr =
|
140
|
+
admin_options_ptr = @native_kafka.with_inner do |inner|
|
141
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_new(inner, Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_DELETETOPICS)
|
142
|
+
end
|
128
143
|
Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, delete_topic_handle.to_ptr)
|
129
144
|
|
130
145
|
begin
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
146
|
+
@native_kafka.with_inner do |inner|
|
147
|
+
Rdkafka::Bindings.rd_kafka_DeleteTopics(
|
148
|
+
inner,
|
149
|
+
topics_array_ptr,
|
150
|
+
1,
|
151
|
+
admin_options_ptr,
|
152
|
+
queue_ptr
|
153
|
+
)
|
154
|
+
end
|
138
155
|
rescue Exception
|
139
156
|
DeleteTopicHandle.remove(delete_topic_handle.to_ptr.address)
|
140
157
|
raise
|
@@ -149,7 +166,7 @@ module Rdkafka
|
|
149
166
|
|
150
167
|
private
|
151
168
|
def closed_admin_check(method)
|
152
|
-
raise Rdkafka::ClosedAdminError.new(method) if
|
169
|
+
raise Rdkafka::ClosedAdminError.new(method) if closed?
|
153
170
|
end
|
154
171
|
end
|
155
172
|
end
|
data/lib/rdkafka/bindings.rb
CHANGED
@@ -41,10 +41,10 @@ module Rdkafka
|
|
41
41
|
|
42
42
|
# Metadata
|
43
43
|
|
44
|
-
attach_function :rd_kafka_memberid, [:pointer], :string
|
45
|
-
attach_function :rd_kafka_clusterid, [:pointer], :string
|
46
|
-
attach_function :rd_kafka_metadata, [:pointer, :int, :pointer, :pointer, :int], :int
|
47
|
-
attach_function :rd_kafka_metadata_destroy, [:pointer], :void
|
44
|
+
attach_function :rd_kafka_memberid, [:pointer], :string, blocking: true
|
45
|
+
attach_function :rd_kafka_clusterid, [:pointer], :string, blocking: true
|
46
|
+
attach_function :rd_kafka_metadata, [:pointer, :int, :pointer, :pointer, :int], :int, blocking: true
|
47
|
+
attach_function :rd_kafka_metadata_destroy, [:pointer], :void, blocking: true
|
48
48
|
|
49
49
|
# Message struct
|
50
50
|
|
@@ -169,26 +169,27 @@ module Rdkafka
|
|
169
169
|
]
|
170
170
|
|
171
171
|
attach_function :rd_kafka_new, [:kafka_type, :pointer, :pointer, :int], :pointer
|
172
|
+
|
172
173
|
attach_function :rd_kafka_destroy, [:pointer], :void
|
173
174
|
|
174
175
|
# Consumer
|
175
176
|
|
176
|
-
attach_function :rd_kafka_subscribe, [:pointer, :pointer], :int
|
177
|
-
attach_function :rd_kafka_unsubscribe, [:pointer], :int
|
178
|
-
attach_function :rd_kafka_subscription, [:pointer, :pointer], :int
|
179
|
-
attach_function :rd_kafka_assign, [:pointer, :pointer], :int
|
180
|
-
attach_function :rd_kafka_incremental_assign, [:pointer, :pointer], :int
|
181
|
-
attach_function :rd_kafka_incremental_unassign, [:pointer, :pointer], :int
|
182
|
-
attach_function :rd_kafka_assignment, [:pointer, :pointer], :int
|
183
|
-
attach_function :rd_kafka_committed, [:pointer, :pointer, :int], :int
|
177
|
+
attach_function :rd_kafka_subscribe, [:pointer, :pointer], :int, blocking: true
|
178
|
+
attach_function :rd_kafka_unsubscribe, [:pointer], :int, blocking: true
|
179
|
+
attach_function :rd_kafka_subscription, [:pointer, :pointer], :int, blocking: true
|
180
|
+
attach_function :rd_kafka_assign, [:pointer, :pointer], :int, blocking: true
|
181
|
+
attach_function :rd_kafka_incremental_assign, [:pointer, :pointer], :int, blocking: true
|
182
|
+
attach_function :rd_kafka_incremental_unassign, [:pointer, :pointer], :int, blocking: true
|
183
|
+
attach_function :rd_kafka_assignment, [:pointer, :pointer], :int, blocking: true
|
184
|
+
attach_function :rd_kafka_committed, [:pointer, :pointer, :int], :int, blocking: true
|
184
185
|
attach_function :rd_kafka_commit, [:pointer, :pointer, :bool], :int, blocking: true
|
185
|
-
attach_function :rd_kafka_poll_set_consumer, [:pointer], :void
|
186
|
+
attach_function :rd_kafka_poll_set_consumer, [:pointer], :void, blocking: true
|
186
187
|
attach_function :rd_kafka_consumer_poll, [:pointer, :int], :pointer, blocking: true
|
187
188
|
attach_function :rd_kafka_consumer_close, [:pointer], :void, blocking: true
|
188
|
-
attach_function :rd_kafka_offset_store, [:pointer, :int32, :int64], :int
|
189
|
-
attach_function :rd_kafka_pause_partitions, [:pointer, :pointer], :int
|
190
|
-
attach_function :rd_kafka_resume_partitions, [:pointer, :pointer], :int
|
191
|
-
attach_function :rd_kafka_seek, [:pointer, :int32, :int64, :int], :int
|
189
|
+
attach_function :rd_kafka_offset_store, [:pointer, :int32, :int64], :int, blocking: true
|
190
|
+
attach_function :rd_kafka_pause_partitions, [:pointer, :pointer], :int, blocking: true
|
191
|
+
attach_function :rd_kafka_resume_partitions, [:pointer, :pointer], :int, blocking: true
|
192
|
+
attach_function :rd_kafka_seek, [:pointer, :int32, :int64, :int], :int, blocking: true
|
192
193
|
|
193
194
|
# Headers
|
194
195
|
attach_function :rd_kafka_header_get_all, [:pointer, :size_t, :pointer, :pointer, SizePtr], :int
|
@@ -197,7 +198,7 @@ module Rdkafka
|
|
197
198
|
# Rebalance
|
198
199
|
|
199
200
|
callback :rebalance_cb_function, [:pointer, :int, :pointer, :pointer], :void
|
200
|
-
attach_function :rd_kafka_conf_set_rebalance_cb, [:pointer, :rebalance_cb_function], :void
|
201
|
+
attach_function :rd_kafka_conf_set_rebalance_cb, [:pointer, :rebalance_cb_function], :void, blocking: true
|
201
202
|
|
202
203
|
RebalanceCallback = FFI::Function.new(
|
203
204
|
:void, [:pointer, :int, :pointer, :pointer]
|
@@ -255,7 +256,7 @@ module Rdkafka
|
|
255
256
|
|
256
257
|
RD_KAFKA_MSG_F_COPY = 0x2
|
257
258
|
|
258
|
-
attach_function :rd_kafka_producev, [:pointer, :varargs], :int
|
259
|
+
attach_function :rd_kafka_producev, [:pointer, :varargs], :int, blocking: true
|
259
260
|
callback :delivery_cb, [:pointer, :pointer, :pointer], :void
|
260
261
|
attach_function :rd_kafka_conf_set_dr_msg_cb, [:pointer, :delivery_cb], :void
|
261
262
|
|
@@ -282,23 +283,23 @@ module Rdkafka
|
|
282
283
|
RD_KAFKA_ADMIN_OP_CREATETOPICS = 1 # rd_kafka_admin_op_t
|
283
284
|
RD_KAFKA_EVENT_CREATETOPICS_RESULT = 100 # rd_kafka_event_type_t
|
284
285
|
|
285
|
-
attach_function :rd_kafka_CreateTopics, [:pointer, :pointer, :size_t, :pointer, :pointer], :void
|
286
|
-
attach_function :rd_kafka_NewTopic_new, [:pointer, :size_t, :size_t, :pointer, :size_t], :pointer
|
287
|
-
attach_function :rd_kafka_NewTopic_set_config, [:pointer, :string, :string], :int32
|
288
|
-
attach_function :rd_kafka_NewTopic_destroy, [:pointer], :void
|
289
|
-
attach_function :rd_kafka_event_CreateTopics_result, [:pointer], :pointer
|
290
|
-
attach_function :rd_kafka_CreateTopics_result_topics, [:pointer, :pointer], :pointer
|
286
|
+
attach_function :rd_kafka_CreateTopics, [:pointer, :pointer, :size_t, :pointer, :pointer], :void, blocking: true
|
287
|
+
attach_function :rd_kafka_NewTopic_new, [:pointer, :size_t, :size_t, :pointer, :size_t], :pointer, blocking: true
|
288
|
+
attach_function :rd_kafka_NewTopic_set_config, [:pointer, :string, :string], :int32, blocking: true
|
289
|
+
attach_function :rd_kafka_NewTopic_destroy, [:pointer], :void, blocking: true
|
290
|
+
attach_function :rd_kafka_event_CreateTopics_result, [:pointer], :pointer, blocking: true
|
291
|
+
attach_function :rd_kafka_CreateTopics_result_topics, [:pointer, :pointer], :pointer, blocking: true
|
291
292
|
|
292
293
|
# Delete Topics
|
293
294
|
|
294
295
|
RD_KAFKA_ADMIN_OP_DELETETOPICS = 2 # rd_kafka_admin_op_t
|
295
296
|
RD_KAFKA_EVENT_DELETETOPICS_RESULT = 101 # rd_kafka_event_type_t
|
296
297
|
|
297
|
-
attach_function :rd_kafka_DeleteTopics, [:pointer, :pointer, :size_t, :pointer, :pointer], :int32
|
298
|
-
attach_function :rd_kafka_DeleteTopic_new, [:pointer], :pointer
|
299
|
-
attach_function :rd_kafka_DeleteTopic_destroy, [:pointer], :void
|
300
|
-
attach_function :rd_kafka_event_DeleteTopics_result, [:pointer], :pointer
|
301
|
-
attach_function :rd_kafka_DeleteTopics_result_topics, [:pointer, :pointer], :pointer
|
298
|
+
attach_function :rd_kafka_DeleteTopics, [:pointer, :pointer, :size_t, :pointer, :pointer], :int32, blocking: true
|
299
|
+
attach_function :rd_kafka_DeleteTopic_new, [:pointer], :pointer, blocking: true
|
300
|
+
attach_function :rd_kafka_DeleteTopic_destroy, [:pointer], :void, blocking: true
|
301
|
+
attach_function :rd_kafka_event_DeleteTopics_result, [:pointer], :pointer, blocking: true
|
302
|
+
attach_function :rd_kafka_DeleteTopics_result_topics, [:pointer, :pointer], :pointer, blocking: true
|
302
303
|
|
303
304
|
# Background Queue and Callback
|
304
305
|
|
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.
|
data/lib/rdkafka/consumer.rb
CHANGED
@@ -26,15 +26,16 @@ module Rdkafka
|
|
26
26
|
# @return [nil]
|
27
27
|
def close
|
28
28
|
return if closed?
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
ObjectSpace.undefine_finalizer(self)
|
30
|
+
@native_kafka.with_inner do |inner|
|
31
|
+
Rdkafka::Bindings.rd_kafka_consumer_close(inner)
|
32
|
+
end
|
33
|
+
@native_kafka.close
|
33
34
|
end
|
34
35
|
|
35
36
|
# Whether this consumer has closed
|
36
37
|
def closed?
|
37
|
-
@native_kafka.
|
38
|
+
@native_kafka.closed?
|
38
39
|
end
|
39
40
|
|
40
41
|
# Subscribe to one or more topics letting Kafka handle partition assignments.
|
@@ -55,7 +56,9 @@ module Rdkafka
|
|
55
56
|
end
|
56
57
|
|
57
58
|
# Subscribe to topic partition list and check this was successful
|
58
|
-
response =
|
59
|
+
response = @native_kafka.with_inner do |inner|
|
60
|
+
Rdkafka::Bindings.rd_kafka_subscribe(inner, tpl)
|
61
|
+
end
|
59
62
|
if response != 0
|
60
63
|
raise Rdkafka::RdkafkaError.new(response, "Error subscribing to '#{topics.join(', ')}'")
|
61
64
|
end
|
@@ -71,7 +74,9 @@ module Rdkafka
|
|
71
74
|
def unsubscribe
|
72
75
|
closed_consumer_check(__method__)
|
73
76
|
|
74
|
-
response =
|
77
|
+
response = @native_kafka.with_inner do |inner|
|
78
|
+
Rdkafka::Bindings.rd_kafka_unsubscribe(inner)
|
79
|
+
end
|
75
80
|
if response != 0
|
76
81
|
raise Rdkafka::RdkafkaError.new(response)
|
77
82
|
end
|
@@ -94,7 +99,9 @@ module Rdkafka
|
|
94
99
|
tpl = list.to_native_tpl
|
95
100
|
|
96
101
|
begin
|
97
|
-
response =
|
102
|
+
response = @native_kafka.with_inner do |inner|
|
103
|
+
Rdkafka::Bindings.rd_kafka_pause_partitions(inner, tpl)
|
104
|
+
end
|
98
105
|
|
99
106
|
if response != 0
|
100
107
|
list = TopicPartitionList.from_native_tpl(tpl)
|
@@ -122,7 +129,9 @@ module Rdkafka
|
|
122
129
|
tpl = list.to_native_tpl
|
123
130
|
|
124
131
|
begin
|
125
|
-
response =
|
132
|
+
response = @native_kafka.with_inner do |inner|
|
133
|
+
Rdkafka::Bindings.rd_kafka_resume_partitions(inner, tpl)
|
134
|
+
end
|
126
135
|
if response != 0
|
127
136
|
raise Rdkafka::RdkafkaError.new(response, "Error resume '#{list.to_h}'")
|
128
137
|
end
|
@@ -140,7 +149,9 @@ module Rdkafka
|
|
140
149
|
closed_consumer_check(__method__)
|
141
150
|
|
142
151
|
ptr = FFI::MemoryPointer.new(:pointer)
|
143
|
-
response =
|
152
|
+
response = @native_kafka.with_inner do |inner|
|
153
|
+
Rdkafka::Bindings.rd_kafka_subscription(inner, ptr)
|
154
|
+
end
|
144
155
|
|
145
156
|
if response != 0
|
146
157
|
raise Rdkafka::RdkafkaError.new(response)
|
@@ -170,7 +181,9 @@ module Rdkafka
|
|
170
181
|
tpl = list.to_native_tpl
|
171
182
|
|
172
183
|
begin
|
173
|
-
response =
|
184
|
+
response = @native_kafka.with_inner do |inner|
|
185
|
+
Rdkafka::Bindings.rd_kafka_assign(inner, tpl)
|
186
|
+
end
|
174
187
|
if response != 0
|
175
188
|
raise Rdkafka::RdkafkaError.new(response, "Error assigning '#{list.to_h}'")
|
176
189
|
end
|
@@ -188,7 +201,9 @@ module Rdkafka
|
|
188
201
|
closed_consumer_check(__method__)
|
189
202
|
|
190
203
|
ptr = FFI::MemoryPointer.new(:pointer)
|
191
|
-
response =
|
204
|
+
response = @native_kafka.with_inner do |inner|
|
205
|
+
Rdkafka::Bindings.rd_kafka_assignment(inner, ptr)
|
206
|
+
end
|
192
207
|
if response != 0
|
193
208
|
raise Rdkafka::RdkafkaError.new(response)
|
194
209
|
end
|
@@ -227,7 +242,9 @@ module Rdkafka
|
|
227
242
|
tpl = list.to_native_tpl
|
228
243
|
|
229
244
|
begin
|
230
|
-
response =
|
245
|
+
response = @native_kafka.with_inner do |inner|
|
246
|
+
Rdkafka::Bindings.rd_kafka_committed(inner, tpl, timeout_ms)
|
247
|
+
end
|
231
248
|
if response != 0
|
232
249
|
raise Rdkafka::RdkafkaError.new(response)
|
233
250
|
end
|
@@ -252,14 +269,16 @@ module Rdkafka
|
|
252
269
|
low = FFI::MemoryPointer.new(:int64, 1)
|
253
270
|
high = FFI::MemoryPointer.new(:int64, 1)
|
254
271
|
|
255
|
-
response =
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
272
|
+
response = @native_kafka.with_inner do |inner|
|
273
|
+
Rdkafka::Bindings.rd_kafka_query_watermark_offsets(
|
274
|
+
inner,
|
275
|
+
topic,
|
276
|
+
partition,
|
277
|
+
low,
|
278
|
+
high,
|
279
|
+
timeout_ms,
|
280
|
+
)
|
281
|
+
end
|
263
282
|
if response != 0
|
264
283
|
raise Rdkafka::RdkafkaError.new(response, "Error querying watermark offsets for partition #{partition} of #{topic}")
|
265
284
|
end
|
@@ -307,7 +326,9 @@ module Rdkafka
|
|
307
326
|
# @return [String, nil]
|
308
327
|
def cluster_id
|
309
328
|
closed_consumer_check(__method__)
|
310
|
-
|
329
|
+
@native_kafka.with_inner do |inner|
|
330
|
+
Rdkafka::Bindings.rd_kafka_clusterid(inner)
|
331
|
+
end
|
311
332
|
end
|
312
333
|
|
313
334
|
# Returns this client's broker-assigned group member id
|
@@ -317,7 +338,9 @@ module Rdkafka
|
|
317
338
|
# @return [String, nil]
|
318
339
|
def member_id
|
319
340
|
closed_consumer_check(__method__)
|
320
|
-
|
341
|
+
@native_kafka.with_inner do |inner|
|
342
|
+
Rdkafka::Bindings.rd_kafka_memberid(inner)
|
343
|
+
end
|
321
344
|
end
|
322
345
|
|
323
346
|
# Store offset of a message to be used in the next commit of this consumer
|
@@ -334,11 +357,13 @@ module Rdkafka
|
|
334
357
|
|
335
358
|
# rd_kafka_offset_store is one of the few calls that does not support
|
336
359
|
# a string as the topic, so create a native topic for it.
|
337
|
-
native_topic =
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
360
|
+
native_topic = @native_kafka.with_inner do |inner|
|
361
|
+
Rdkafka::Bindings.rd_kafka_topic_new(
|
362
|
+
inner,
|
363
|
+
message.topic,
|
364
|
+
nil
|
365
|
+
)
|
366
|
+
end
|
342
367
|
response = Rdkafka::Bindings.rd_kafka_offset_store(
|
343
368
|
native_topic,
|
344
369
|
message.partition,
|
@@ -366,11 +391,13 @@ module Rdkafka
|
|
366
391
|
|
367
392
|
# rd_kafka_offset_store is one of the few calls that does not support
|
368
393
|
# a string as the topic, so create a native topic for it.
|
369
|
-
native_topic =
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
394
|
+
native_topic = @native_kafka.with_inner do |inner|
|
395
|
+
Rdkafka::Bindings.rd_kafka_topic_new(
|
396
|
+
inner,
|
397
|
+
message.topic,
|
398
|
+
nil
|
399
|
+
)
|
400
|
+
end
|
374
401
|
response = Rdkafka::Bindings.rd_kafka_seek(
|
375
402
|
native_topic,
|
376
403
|
message.partition,
|
@@ -411,7 +438,9 @@ module Rdkafka
|
|
411
438
|
tpl = list ? list.to_native_tpl : nil
|
412
439
|
|
413
440
|
begin
|
414
|
-
response =
|
441
|
+
response = @native_kafka.with_inner do |inner|
|
442
|
+
Rdkafka::Bindings.rd_kafka_commit(inner, tpl, async)
|
443
|
+
end
|
415
444
|
if response != 0
|
416
445
|
raise Rdkafka::RdkafkaError.new(response)
|
417
446
|
end
|
@@ -430,7 +459,9 @@ module Rdkafka
|
|
430
459
|
def poll(timeout_ms)
|
431
460
|
closed_consumer_check(__method__)
|
432
461
|
|
433
|
-
message_ptr =
|
462
|
+
message_ptr = @native_kafka.with_inner do |inner|
|
463
|
+
Rdkafka::Bindings.rd_kafka_consumer_poll(inner, timeout_ms)
|
464
|
+
end
|
434
465
|
if message_ptr.null?
|
435
466
|
nil
|
436
467
|
else
|
@@ -445,7 +476,7 @@ module Rdkafka
|
|
445
476
|
end
|
446
477
|
ensure
|
447
478
|
# Clean up rdkafka message if there is one
|
448
|
-
if
|
479
|
+
if message_ptr && !message_ptr.null?
|
449
480
|
Rdkafka::Bindings.rd_kafka_message_destroy(message_ptr)
|
450
481
|
end
|
451
482
|
end
|
data/lib/rdkafka/native_kafka.rb
CHANGED
@@ -4,25 +4,42 @@ 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
|
+
# Lock around external access
|
10
|
+
@access_mutex = Mutex.new
|
11
|
+
# Lock around internal polling
|
12
|
+
@poll_mutex = Mutex.new
|
9
13
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
if run_polling_thread
|
15
|
+
# Start thread to poll client for delivery callbacks,
|
16
|
+
# not used in consumer.
|
17
|
+
@polling_thread = Thread.new do
|
18
|
+
loop do
|
19
|
+
@poll_mutex.synchronize do
|
20
|
+
Rdkafka::Bindings.rd_kafka_poll(inner, 100)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Exit thread if closing and the poll queue is empty
|
24
|
+
if Thread.current[:closing] && Rdkafka::Bindings.rd_kafka_outq_len(inner) == 0
|
25
|
+
break
|
26
|
+
end
|
17
27
|
end
|
18
28
|
end
|
29
|
+
|
30
|
+
@polling_thread.abort_on_exception = true
|
31
|
+
@polling_thread[:closing] = false
|
19
32
|
end
|
20
|
-
|
21
|
-
@
|
33
|
+
|
34
|
+
@closing = false
|
22
35
|
end
|
23
36
|
|
24
|
-
def
|
25
|
-
@inner
|
37
|
+
def with_inner
|
38
|
+
return if @inner.nil?
|
39
|
+
|
40
|
+
@access_mutex.synchronize do
|
41
|
+
yield @inner
|
42
|
+
end
|
26
43
|
end
|
27
44
|
|
28
45
|
def finalizer
|
@@ -30,22 +47,36 @@ module Rdkafka
|
|
30
47
|
end
|
31
48
|
|
32
49
|
def closed?
|
33
|
-
@inner.nil?
|
50
|
+
@closing || @inner.nil?
|
34
51
|
end
|
35
52
|
|
36
53
|
def close(object_id=nil)
|
37
54
|
return if closed?
|
38
55
|
|
39
|
-
|
40
|
-
Rdkafka::Bindings.rd_kafka_flush(@inner, 30 * 1000)
|
56
|
+
@access_mutex.lock
|
41
57
|
|
42
|
-
# Indicate to
|
43
|
-
@
|
44
|
-
# Wait for the polling thread to finish up
|
45
|
-
@polling_thread.join
|
58
|
+
# Indicate to the outside world that we are closing
|
59
|
+
@closing = true
|
46
60
|
|
47
|
-
|
61
|
+
if @polling_thread
|
62
|
+
# Indicate to polling thread that we're closing
|
63
|
+
@polling_thread[:closing] = true
|
48
64
|
|
65
|
+
# Wait for the polling thread to finish up,
|
66
|
+
# this can be aborted in practice if this
|
67
|
+
# code runs from a finalizer.
|
68
|
+
@polling_thread.join
|
69
|
+
end
|
70
|
+
|
71
|
+
# Destroy the client after locking both mutexes
|
72
|
+
@poll_mutex.lock
|
73
|
+
|
74
|
+
# This check prevents a race condition, where we would enter the close in two threads
|
75
|
+
# and after unlocking the primary one that hold the lock but finished, ours would be unlocked
|
76
|
+
# and would continue to run, trying to destroy inner twice
|
77
|
+
return unless @inner
|
78
|
+
|
79
|
+
Rdkafka::Bindings.rd_kafka_destroy(@inner)
|
49
80
|
@inner = nil
|
50
81
|
end
|
51
82
|
end
|
data/lib/rdkafka/producer.rb
CHANGED
@@ -40,11 +40,28 @@ 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
|
+
#
|
56
|
+
# @param timeout_ms [Integer] how long should we wait for flush of all messages
|
57
|
+
def flush(timeout_ms=5_000)
|
58
|
+
closed_producer_check(__method__)
|
59
|
+
|
60
|
+
@native_kafka.with_inner do |inner|
|
61
|
+
Rdkafka::Bindings.rd_kafka_flush(inner, timeout_ms)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
48
65
|
# Partition count for a given topic.
|
49
66
|
# NOTE: If 'allow.auto.create.topics' is set to true in the broker, the topic will be auto-created after returning nil.
|
50
67
|
#
|
@@ -53,7 +70,9 @@ module Rdkafka
|
|
53
70
|
# @return partition count [Integer,nil]
|
54
71
|
def partition_count(topic)
|
55
72
|
closed_producer_check(__method__)
|
56
|
-
|
73
|
+
@native_kafka.with_inner do |inner|
|
74
|
+
Rdkafka::Metadata.new(inner, topic).topics&.first[:partition_count]
|
75
|
+
end
|
57
76
|
end
|
58
77
|
|
59
78
|
# Produces a message to a Kafka topic. The message is added to rdkafka's queue, call {DeliveryHandle#wait wait} on the returned delivery handle to make sure it is delivered.
|
@@ -144,10 +163,12 @@ module Rdkafka
|
|
144
163
|
args << :int << Rdkafka::Bindings::RD_KAFKA_VTYPE_END
|
145
164
|
|
146
165
|
# Produce the message
|
147
|
-
response =
|
148
|
-
|
149
|
-
|
150
|
-
|
166
|
+
response = @native_kafka.with_inner do |inner|
|
167
|
+
Rdkafka::Bindings.rd_kafka_producev(
|
168
|
+
inner,
|
169
|
+
*args
|
170
|
+
)
|
171
|
+
end
|
151
172
|
|
152
173
|
# Raise error if the produce call was not successful
|
153
174
|
if response != 0
|
@@ -173,7 +194,7 @@ module Rdkafka
|
|
173
194
|
|
174
195
|
private
|
175
196
|
def closed_producer_check(method)
|
176
|
-
raise Rdkafka::ClosedProducerError.new(method) if
|
197
|
+
raise Rdkafka::ClosedProducerError.new(method) if closed?
|
177
198
|
end
|
178
199
|
end
|
179
200
|
end
|
data/lib/rdkafka/version.rb
CHANGED
data/spec/rdkafka/config_spec.rb
CHANGED
@@ -151,22 +151,23 @@ describe Rdkafka::Config do
|
|
151
151
|
end
|
152
152
|
|
153
153
|
it "allows string partitioner key" do
|
154
|
-
expect(Rdkafka::Producer).to receive(:new).with(kind_of(Rdkafka::NativeKafka), "murmur2")
|
154
|
+
expect(Rdkafka::Producer).to receive(:new).with(kind_of(Rdkafka::NativeKafka), "murmur2").and_call_original
|
155
155
|
config = Rdkafka::Config.new("partitioner" => "murmur2")
|
156
|
-
config.producer
|
156
|
+
config.producer.close
|
157
157
|
end
|
158
158
|
|
159
159
|
it "allows symbol partitioner key" do
|
160
|
-
expect(Rdkafka::Producer).to receive(:new).with(kind_of(Rdkafka::NativeKafka), "murmur2")
|
160
|
+
expect(Rdkafka::Producer).to receive(:new).with(kind_of(Rdkafka::NativeKafka), "murmur2").and_call_original
|
161
161
|
config = Rdkafka::Config.new(:partitioner => "murmur2")
|
162
|
-
config.producer
|
162
|
+
config.producer.close
|
163
163
|
end
|
164
164
|
|
165
165
|
it "should allow configuring zstd compression" do
|
166
166
|
config = Rdkafka::Config.new('compression.codec' => 'zstd')
|
167
167
|
begin
|
168
|
-
|
169
|
-
|
168
|
+
producer = config.producer
|
169
|
+
expect(producer).to be_a Rdkafka::Producer
|
170
|
+
producer.close
|
170
171
|
rescue Rdkafka::Config::ConfigError => ex
|
171
172
|
pending "Zstd compression not supported on this machine"
|
172
173
|
raise ex
|
@@ -55,7 +55,7 @@ describe Rdkafka::Consumer do
|
|
55
55
|
|
56
56
|
describe "#pause and #resume" do
|
57
57
|
context "subscription" do
|
58
|
-
let(:timeout) {
|
58
|
+
let(:timeout) { 2000 }
|
59
59
|
|
60
60
|
before { consumer.subscribe("consume_test_topic") }
|
61
61
|
after { consumer.unsubscribe }
|
@@ -738,7 +738,8 @@ describe Rdkafka::Consumer do
|
|
738
738
|
#
|
739
739
|
# This is, in effect, an integration test and the subsequent specs are
|
740
740
|
# unit tests.
|
741
|
-
|
741
|
+
admin = rdkafka_config.admin
|
742
|
+
create_topic_handle = admin.create_topic(topic_name, 1, 1)
|
742
743
|
create_topic_handle.wait(max_wait_timeout: 15.0)
|
743
744
|
consumer.subscribe(topic_name)
|
744
745
|
produce_n 42
|
@@ -751,6 +752,7 @@ describe Rdkafka::Consumer do
|
|
751
752
|
expect(all_yields.flatten.size).to eq 42
|
752
753
|
expect(all_yields.size).to be > 4
|
753
754
|
expect(all_yields.flatten.map(&:key)).to eq (0..41).map { |x| x.to_s }
|
755
|
+
admin.close
|
754
756
|
end
|
755
757
|
|
756
758
|
it "should batch poll results and yield arrays of messages" do
|
@@ -793,13 +795,15 @@ describe Rdkafka::Consumer do
|
|
793
795
|
end
|
794
796
|
|
795
797
|
it "should yield [] if nothing is received before the timeout" do
|
796
|
-
|
798
|
+
admin = rdkafka_config.admin
|
799
|
+
create_topic_handle = admin.create_topic(topic_name, 1, 1)
|
797
800
|
create_topic_handle.wait(max_wait_timeout: 15.0)
|
798
801
|
consumer.subscribe(topic_name)
|
799
802
|
consumer.each_batch do |batch|
|
800
803
|
expect(batch).to eq([])
|
801
804
|
break
|
802
805
|
end
|
806
|
+
admin.close
|
803
807
|
end
|
804
808
|
|
805
809
|
it "should yield batchs of max_items in size if messages are already fetched" do
|
@@ -876,6 +880,7 @@ describe Rdkafka::Consumer do
|
|
876
880
|
expect(batches_yielded.first.size).to eq 2
|
877
881
|
expect(exceptions_yielded.flatten.size).to eq 1
|
878
882
|
expect(exceptions_yielded.flatten.first).to be_instance_of(Rdkafka::RdkafkaError)
|
883
|
+
consumer.close
|
879
884
|
end
|
880
885
|
end
|
881
886
|
|
@@ -917,6 +922,7 @@ describe Rdkafka::Consumer do
|
|
917
922
|
expect(each_batch_iterations).to eq 0
|
918
923
|
expect(batches_yielded.size).to eq 0
|
919
924
|
expect(exceptions_yielded.size).to eq 0
|
925
|
+
consumer.close
|
920
926
|
end
|
921
927
|
end
|
922
928
|
end
|
@@ -8,12 +8,9 @@ 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
|
-
allow(Rdkafka::Bindings).to receive(:rd_kafka_poll).with(instance_of(FFI::Pointer), 250).and_call_original
|
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(:rd_kafka_destroy)
|
17
14
|
allow(Thread).to receive(:new).and_return(thread)
|
18
15
|
|
19
16
|
allow(thread).to receive(:[]=).with(:closing, anything)
|
@@ -21,6 +18,8 @@ describe Rdkafka::NativeKafka do
|
|
21
18
|
allow(thread).to receive(:abort_on_exception=).with(anything)
|
22
19
|
end
|
23
20
|
|
21
|
+
after { client.close }
|
22
|
+
|
24
23
|
context "defaults" do
|
25
24
|
it "sets the thread to abort on exception" do
|
26
25
|
expect(thread).to receive(:abort_on_exception=).with(true)
|
@@ -41,32 +40,12 @@ describe Rdkafka::NativeKafka do
|
|
41
40
|
|
42
41
|
client
|
43
42
|
end
|
44
|
-
|
45
|
-
it "polls the native with default 250ms timeout" do
|
46
|
-
polling_loop_expects do
|
47
|
-
expect(Rdkafka::Bindings).to receive(:rd_kafka_poll).with(instance_of(FFI::Pointer), 250).at_least(:once)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
it "check the out queue of native client" do
|
52
|
-
polling_loop_expects do
|
53
|
-
expect(Rdkafka::Bindings).to receive(:rd_kafka_outq_len).with(native).at_least(:once)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def polling_loop_expects(&block)
|
59
|
-
Thread.current[:closing] = true # this forces the loop break with line #12
|
60
|
-
|
61
|
-
allow(Thread).to receive(:new).and_yield do |_|
|
62
|
-
block.call
|
63
|
-
end.and_return(thread)
|
64
|
-
|
65
|
-
client
|
66
43
|
end
|
67
44
|
|
68
|
-
it "exposes inner client" do
|
69
|
-
|
45
|
+
it "exposes the inner client" do
|
46
|
+
client.with_inner do |inner|
|
47
|
+
expect(inner).to eq(native)
|
48
|
+
end
|
70
49
|
end
|
71
50
|
|
72
51
|
context "when client was not yet closed (`nil`)" do
|
@@ -76,7 +55,7 @@ describe Rdkafka::NativeKafka do
|
|
76
55
|
|
77
56
|
context "and attempt to close" do
|
78
57
|
it "calls the `destroy` binding" do
|
79
|
-
expect(Rdkafka::Bindings).to receive(:rd_kafka_destroy).with(native)
|
58
|
+
expect(Rdkafka::Bindings).to receive(:rd_kafka_destroy).with(native).and_call_original
|
80
59
|
|
81
60
|
client.close
|
82
61
|
end
|
@@ -96,7 +75,6 @@ describe Rdkafka::NativeKafka do
|
|
96
75
|
it "closes and unassign the native client" do
|
97
76
|
client.close
|
98
77
|
|
99
|
-
expect(client.inner).to eq(nil)
|
100
78
|
expect(client.closed?).to eq(true)
|
101
79
|
end
|
102
80
|
end
|
@@ -111,7 +89,7 @@ describe Rdkafka::NativeKafka do
|
|
111
89
|
|
112
90
|
context "and attempt to close again" do
|
113
91
|
it "does not call the `destroy` binding" do
|
114
|
-
expect(Rdkafka::Bindings).not_to receive(:
|
92
|
+
expect(Rdkafka::Bindings).not_to receive(:rd_kafka_destroy_flags)
|
115
93
|
|
116
94
|
client.close
|
117
95
|
end
|
@@ -131,7 +109,6 @@ describe Rdkafka::NativeKafka do
|
|
131
109
|
it "does not close and unassign the native client again" do
|
132
110
|
client.close
|
133
111
|
|
134
|
-
expect(client.inner).to eq(nil)
|
135
112
|
expect(client.closed?).to eq(true)
|
136
113
|
end
|
137
114
|
end
|
@@ -185,7 +185,8 @@ describe Rdkafka::Producer do
|
|
185
185
|
expect(report.partition).to eq 1
|
186
186
|
expect(report.offset).to be >= 0
|
187
187
|
|
188
|
-
#
|
188
|
+
# Flush and close producer
|
189
|
+
producer.flush
|
189
190
|
producer.close
|
190
191
|
|
191
192
|
# Consume message and verify its content
|
@@ -459,10 +460,10 @@ describe Rdkafka::Producer do
|
|
459
460
|
# wait for and check the message in the main process.
|
460
461
|
reader, writer = IO.pipe
|
461
462
|
|
462
|
-
fork do
|
463
|
+
pid = fork do
|
463
464
|
reader.close
|
464
465
|
|
465
|
-
#
|
466
|
+
# Avoid sharing the client between processes.
|
466
467
|
producer = rdkafka_producer_config.producer
|
467
468
|
|
468
469
|
handle = producer.produce(
|
@@ -481,8 +482,10 @@ describe Rdkafka::Producer do
|
|
481
482
|
|
482
483
|
writer.write(report_json)
|
483
484
|
writer.close
|
485
|
+
producer.flush
|
484
486
|
producer.close
|
485
487
|
end
|
488
|
+
Process.wait(pid)
|
486
489
|
|
487
490
|
writer.close
|
488
491
|
report_hash = JSON.parse(reader.read)
|
data/spec/spec_helper.rb
CHANGED
@@ -73,7 +73,7 @@ def new_native_topic(topic_name="topic_name", native_client: )
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def wait_for_message(topic:, delivery_report:, timeout_in_seconds: 30, consumer: nil)
|
76
|
-
new_consumer =
|
76
|
+
new_consumer = consumer.nil?
|
77
77
|
consumer ||= rdkafka_consumer_config.consumer
|
78
78
|
consumer.subscribe(topic)
|
79
79
|
timeout = Time.now.to_i + timeout_in_seconds
|
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.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thijs Cadier
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -139,8 +139,7 @@ dependencies:
|
|
139
139
|
description: Modern Kafka client library for Ruby based on librdkafka
|
140
140
|
email:
|
141
141
|
- thijs@appsignal.com
|
142
|
-
executables:
|
143
|
-
- console
|
142
|
+
executables: []
|
144
143
|
extensions:
|
145
144
|
- ext/Rakefile
|
146
145
|
extra_rdoc_files: []
|
@@ -155,7 +154,6 @@ files:
|
|
155
154
|
- LICENSE
|
156
155
|
- README.md
|
157
156
|
- Rakefile
|
158
|
-
- bin/console
|
159
157
|
- docker-compose.yml
|
160
158
|
- ext/README.md
|
161
159
|
- ext/Rakefile
|
@@ -222,7 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
222
220
|
- !ruby/object:Gem::Version
|
223
221
|
version: 1.3.1
|
224
222
|
requirements: []
|
225
|
-
rubygems_version: 3.
|
223
|
+
rubygems_version: 3.4.1
|
226
224
|
signing_key:
|
227
225
|
specification_version: 4
|
228
226
|
summary: The rdkafka gem is a modern Kafka client library for Ruby based on librdkafka.
|