rdkafka 0.12.0.beta.4 → 0.13.0.beta.1
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 +6 -2
- data/CHANGELOG.md +10 -1
- data/Gemfile +2 -0
- data/Rakefile +2 -0
- data/ext/Rakefile +2 -0
- data/lib/rdkafka/abstract_handle.rb +2 -0
- data/lib/rdkafka/admin/create_topic_handle.rb +2 -0
- data/lib/rdkafka/admin/create_topic_report.rb +2 -0
- data/lib/rdkafka/admin/delete_topic_handle.rb +2 -0
- data/lib/rdkafka/admin/delete_topic_report.rb +2 -0
- data/lib/rdkafka/admin.rb +35 -35
- data/lib/rdkafka/bindings.rb +5 -2
- data/lib/rdkafka/callbacks.rb +7 -1
- data/lib/rdkafka/config.rb +9 -7
- data/lib/rdkafka/consumer/headers.rb +2 -0
- data/lib/rdkafka/consumer/message.rb +2 -0
- data/lib/rdkafka/consumer/partition.rb +2 -0
- data/lib/rdkafka/consumer/topic_partition_list.rb +2 -0
- data/lib/rdkafka/consumer.rb +18 -9
- data/lib/rdkafka/error.rb +9 -0
- data/lib/rdkafka/metadata.rb +2 -0
- data/lib/rdkafka/native_kafka.rb +52 -0
- data/lib/rdkafka/producer/delivery_handle.rb +5 -2
- data/lib/rdkafka/producer/delivery_report.rb +9 -2
- data/lib/rdkafka/producer.rb +30 -13
- data/lib/rdkafka/version.rb +5 -3
- data/lib/rdkafka.rb +3 -1
- data/rdkafka.gemspec +2 -0
- data/spec/rdkafka/abstract_handle_spec.rb +2 -0
- data/spec/rdkafka/admin/create_topic_handle_spec.rb +2 -0
- data/spec/rdkafka/admin/create_topic_report_spec.rb +2 -0
- data/spec/rdkafka/admin/delete_topic_handle_spec.rb +2 -0
- data/spec/rdkafka/admin/delete_topic_report_spec.rb +2 -0
- data/spec/rdkafka/admin_spec.rb +4 -3
- data/spec/rdkafka/bindings_spec.rb +2 -0
- data/spec/rdkafka/callbacks_spec.rb +2 -0
- data/spec/rdkafka/config_spec.rb +14 -0
- data/spec/rdkafka/consumer/message_spec.rb +2 -0
- data/spec/rdkafka/consumer/partition_spec.rb +2 -0
- data/spec/rdkafka/consumer/topic_partition_list_spec.rb +2 -0
- data/spec/rdkafka/consumer_spec.rb +10 -0
- data/spec/rdkafka/error_spec.rb +2 -0
- data/spec/rdkafka/metadata_spec.rb +2 -0
- data/spec/rdkafka/{producer/client_spec.rb → native_kafka_spec.rb} +8 -6
- data/spec/rdkafka/producer/delivery_handle_spec.rb +5 -0
- data/spec/rdkafka/producer/delivery_report_spec.rb +8 -2
- data/spec/rdkafka/producer_spec.rb +82 -3
- data/spec/spec_helper.rb +2 -0
- metadata +9 -9
- data/lib/rdkafka/producer/client.rb +0 -47
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3c5b37efae2485950c8a7627e09dfca50bd7c1264e18ae811b85c3f9ae7d09a
|
4
|
+
data.tar.gz: fb106016aae053f18f53885ae176cdb9e07078c80139146b165231cdd7e490ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2aaaf70e222ad813ec88ce49078f0e643c1860ff8ce93289134d52ac8a7104681c3f66fad48083c3d78223f7f3157dd7019bd41355933141316078bb1c5fd3aa
|
7
|
+
data.tar.gz: a1503635d8e51589db14db327176cbae4f8be9454938e63c96f2bfdcbd258cef1f392479955797e742463cd44ab1951f7596d23adb2b5c06ebf6ff6ab1963442
|
data/.semaphore/semaphore.yml
CHANGED
@@ -9,15 +9,19 @@ agent:
|
|
9
9
|
blocks:
|
10
10
|
- name: Run specs
|
11
11
|
task:
|
12
|
+
prologue:
|
13
|
+
commands:
|
14
|
+
- sudo apt install -y valgrind
|
12
15
|
jobs:
|
13
16
|
- name: bundle exec rspec
|
14
17
|
matrix:
|
15
18
|
- env_var: RUBY_VERSION
|
16
|
-
values: [ "2.6.
|
19
|
+
values: [ "2.6.10", "2.7.6", "3.0.4", "3.1.2"]
|
17
20
|
commands:
|
18
21
|
- sem-version ruby $RUBY_VERSION
|
19
22
|
- checkout
|
20
23
|
- bundle install --path vendor/bundle
|
21
24
|
- cd ext && bundle exec rake && cd ..
|
22
25
|
- docker-compose up -d --no-recreate
|
23
|
-
-
|
26
|
+
- ulimit -c unlimited
|
27
|
+
- valgrind -v bundle exec rspec
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
|
+
# 0.13.0
|
2
|
+
* Add topic name to delivery report (maeve)
|
3
|
+
* Allow string partitioner config (mollyegibson)
|
4
|
+
* Fix documented type for DeliveryReport#error (jimmydo)
|
5
|
+
* Bump librdkafka to 1.9.2 (thijsc)
|
6
|
+
* Use finalizers to cleanly exit producer and admin (thijsc)
|
7
|
+
|
1
8
|
# 0.12.0
|
2
|
-
*
|
9
|
+
* Bump librdkafka to 1.9.0
|
10
|
+
* Fix crash on empty partition key (mensfeld)
|
11
|
+
* Pass the delivery handle to the callback (gvisokinskas)
|
3
12
|
|
4
13
|
# 0.11.0
|
5
14
|
* Upgrade librdkafka to 1.8.2
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
data/ext/Rakefile
CHANGED
data/lib/rdkafka/admin.rb
CHANGED
@@ -1,33 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "objspace"
|
4
|
+
|
1
5
|
module Rdkafka
|
2
6
|
class Admin
|
3
7
|
# @private
|
4
8
|
def initialize(native_kafka)
|
5
9
|
@native_kafka = native_kafka
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
if @closing && Rdkafka::Bindings.rd_kafka_outq_len(@native_kafka) == 0
|
14
|
-
break
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
@polling_thread.abort_on_exception = true
|
10
|
+
|
11
|
+
# Makes sure, that native kafka gets closed before it gets GCed by Ruby
|
12
|
+
ObjectSpace.define_finalizer(self, native_kafka.finalizer)
|
13
|
+
end
|
14
|
+
|
15
|
+
def finalizer
|
16
|
+
->(_) { close }
|
19
17
|
end
|
20
18
|
|
21
19
|
# Close this admin instance
|
22
20
|
def close
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
@closing = true
|
27
|
-
# Wait for the polling thread to finish up
|
28
|
-
@polling_thread.join
|
29
|
-
Rdkafka::Bindings.rd_kafka_destroy(@native_kafka)
|
30
|
-
@native_kafka = nil
|
21
|
+
ObjectSpace.undefine_finalizer(self)
|
22
|
+
|
23
|
+
@native_kafka.close
|
31
24
|
end
|
32
25
|
|
33
26
|
# Create a topic with the given partition count and replication factor
|
@@ -38,6 +31,7 @@ module Rdkafka
|
|
38
31
|
#
|
39
32
|
# @return [CreateTopicHandle] Create topic handle that can be used to wait for the result of creating the topic
|
40
33
|
def create_topic(topic_name, partition_count, replication_factor, topic_config={})
|
34
|
+
closed_admin_check(__method__)
|
41
35
|
|
42
36
|
# Create a rd_kafka_NewTopic_t representing the new topic
|
43
37
|
error_buffer = FFI::MemoryPointer.from_string(" " * 256)
|
@@ -68,7 +62,7 @@ module Rdkafka
|
|
68
62
|
topics_array_ptr.write_array_of_pointer(pointer_array)
|
69
63
|
|
70
64
|
# Get a pointer to the queue that our request will be enqueued on
|
71
|
-
queue_ptr = Rdkafka::Bindings.rd_kafka_queue_get_background(@native_kafka)
|
65
|
+
queue_ptr = Rdkafka::Bindings.rd_kafka_queue_get_background(@native_kafka.inner)
|
72
66
|
if queue_ptr.null?
|
73
67
|
Rdkafka::Bindings.rd_kafka_NewTopic_destroy(new_topic_ptr)
|
74
68
|
raise Rdkafka::Config::ConfigError.new("rd_kafka_queue_get_background was NULL")
|
@@ -79,16 +73,16 @@ module Rdkafka
|
|
79
73
|
create_topic_handle[:pending] = true
|
80
74
|
create_topic_handle[:response] = -1
|
81
75
|
CreateTopicHandle.register(create_topic_handle)
|
82
|
-
admin_options_ptr = Rdkafka::Bindings.rd_kafka_AdminOptions_new(@native_kafka, Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_CREATETOPICS)
|
76
|
+
admin_options_ptr = Rdkafka::Bindings.rd_kafka_AdminOptions_new(@native_kafka.inner, Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_CREATETOPICS)
|
83
77
|
Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, create_topic_handle.to_ptr)
|
84
78
|
|
85
79
|
begin
|
86
80
|
Rdkafka::Bindings.rd_kafka_CreateTopics(
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
81
|
+
@native_kafka.inner,
|
82
|
+
topics_array_ptr,
|
83
|
+
1,
|
84
|
+
admin_options_ptr,
|
85
|
+
queue_ptr
|
92
86
|
)
|
93
87
|
rescue Exception
|
94
88
|
CreateTopicHandle.remove(create_topic_handle.to_ptr.address)
|
@@ -108,6 +102,7 @@ module Rdkafka
|
|
108
102
|
#
|
109
103
|
# @return [DeleteTopicHandle] Delete topic handle that can be used to wait for the result of deleting the topic
|
110
104
|
def delete_topic(topic_name)
|
105
|
+
closed_admin_check(__method__)
|
111
106
|
|
112
107
|
# Create a rd_kafka_DeleteTopic_t representing the topic to be deleted
|
113
108
|
delete_topic_ptr = Rdkafka::Bindings.rd_kafka_DeleteTopic_new(FFI::MemoryPointer.from_string(topic_name))
|
@@ -118,7 +113,7 @@ module Rdkafka
|
|
118
113
|
topics_array_ptr.write_array_of_pointer(pointer_array)
|
119
114
|
|
120
115
|
# Get a pointer to the queue that our request will be enqueued on
|
121
|
-
queue_ptr = Rdkafka::Bindings.rd_kafka_queue_get_background(@native_kafka)
|
116
|
+
queue_ptr = Rdkafka::Bindings.rd_kafka_queue_get_background(@native_kafka.inner)
|
122
117
|
if queue_ptr.null?
|
123
118
|
Rdkafka::Bindings.rd_kafka_DeleteTopic_destroy(delete_topic_ptr)
|
124
119
|
raise Rdkafka::Config::ConfigError.new("rd_kafka_queue_get_background was NULL")
|
@@ -129,16 +124,16 @@ module Rdkafka
|
|
129
124
|
delete_topic_handle[:pending] = true
|
130
125
|
delete_topic_handle[:response] = -1
|
131
126
|
DeleteTopicHandle.register(delete_topic_handle)
|
132
|
-
admin_options_ptr = Rdkafka::Bindings.rd_kafka_AdminOptions_new(@native_kafka, Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_DELETETOPICS)
|
127
|
+
admin_options_ptr = Rdkafka::Bindings.rd_kafka_AdminOptions_new(@native_kafka.inner, Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_DELETETOPICS)
|
133
128
|
Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, delete_topic_handle.to_ptr)
|
134
129
|
|
135
130
|
begin
|
136
131
|
Rdkafka::Bindings.rd_kafka_DeleteTopics(
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
132
|
+
@native_kafka.inner,
|
133
|
+
topics_array_ptr,
|
134
|
+
1,
|
135
|
+
admin_options_ptr,
|
136
|
+
queue_ptr
|
142
137
|
)
|
143
138
|
rescue Exception
|
144
139
|
DeleteTopicHandle.remove(delete_topic_handle.to_ptr.address)
|
@@ -151,5 +146,10 @@ module Rdkafka
|
|
151
146
|
|
152
147
|
delete_topic_handle
|
153
148
|
end
|
149
|
+
|
150
|
+
private
|
151
|
+
def closed_admin_check(method)
|
152
|
+
raise Rdkafka::ClosedAdminError.new(method) if @native_kafka.closed?
|
153
|
+
end
|
154
154
|
end
|
155
155
|
end
|
data/lib/rdkafka/bindings.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "ffi"
|
2
4
|
require "json"
|
3
5
|
require "logger"
|
@@ -33,6 +35,7 @@ module Rdkafka
|
|
33
35
|
|
34
36
|
# Polling
|
35
37
|
|
38
|
+
attach_function :rd_kafka_flush, [:pointer, :int], :void, blocking: true
|
36
39
|
attach_function :rd_kafka_poll, [:pointer, :int], :void, blocking: true
|
37
40
|
attach_function :rd_kafka_outq_len, [:pointer], :int, blocking: true
|
38
41
|
|
@@ -256,11 +259,11 @@ module Rdkafka
|
|
256
259
|
# Return RD_KAFKA_PARTITION_UA(unassigned partition) when partition count is nil/zero.
|
257
260
|
return -1 unless partition_count&.nonzero?
|
258
261
|
|
259
|
-
str_ptr = FFI::MemoryPointer.from_string(str)
|
262
|
+
str_ptr = str.empty? ? FFI::MemoryPointer::NULL : FFI::MemoryPointer.from_string(str)
|
260
263
|
method_name = PARTITIONERS.fetch(partitioner_name) do
|
261
264
|
raise Rdkafka::Config::ConfigError.new("Unknown partitioner: #{partitioner_name}")
|
262
265
|
end
|
263
|
-
public_send(method_name, nil, str_ptr, str.size, partition_count, nil, nil)
|
266
|
+
public_send(method_name, nil, str_ptr, str.size > 0 ? str.size : 1, partition_count, nil, nil)
|
264
267
|
end
|
265
268
|
|
266
269
|
# Create Topics
|
data/lib/rdkafka/callbacks.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rdkafka
|
2
4
|
module Callbacks
|
3
5
|
|
@@ -90,14 +92,18 @@ module Rdkafka
|
|
90
92
|
message = Rdkafka::Bindings::Message.new(message_ptr)
|
91
93
|
delivery_handle_ptr_address = message[:_private].address
|
92
94
|
if delivery_handle = Rdkafka::Producer::DeliveryHandle.remove(delivery_handle_ptr_address)
|
95
|
+
topic_name = Rdkafka::Bindings.rd_kafka_topic_name(message[:rkt])
|
96
|
+
|
93
97
|
# Update delivery handle
|
94
98
|
delivery_handle[:response] = message[:err]
|
95
99
|
delivery_handle[:partition] = message[:partition]
|
96
100
|
delivery_handle[:offset] = message[:offset]
|
101
|
+
delivery_handle[:topic_name] = FFI::MemoryPointer.from_string(topic_name)
|
97
102
|
delivery_handle[:pending] = false
|
103
|
+
|
98
104
|
# Call delivery callback on opaque
|
99
105
|
if opaque = Rdkafka::Config.opaques[opaque_ptr.to_i]
|
100
|
-
opaque.call_delivery_callback(Rdkafka::Producer::DeliveryReport.new(message[:partition], message[:offset], message[:err]))
|
106
|
+
opaque.call_delivery_callback(Rdkafka::Producer::DeliveryReport.new(message[:partition], message[:offset], topic_name, message[:err]), delivery_handle)
|
101
107
|
end
|
102
108
|
end
|
103
109
|
end
|
data/lib/rdkafka/config.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "logger"
|
2
4
|
|
3
5
|
module Rdkafka
|
@@ -30,7 +32,6 @@ module Rdkafka
|
|
30
32
|
@@logger
|
31
33
|
end
|
32
34
|
|
33
|
-
|
34
35
|
# Returns a queue whose contents will be passed to the configured logger. Each entry
|
35
36
|
# should follow the format [Logger::Severity, String]. The benefit over calling the
|
36
37
|
# logger directly is that this is safe to use from trap contexts.
|
@@ -47,7 +48,7 @@ module Rdkafka
|
|
47
48
|
# @return [nil]
|
48
49
|
def self.logger=(logger)
|
49
50
|
raise NoLoggerError if logger.nil?
|
50
|
-
@@logger=logger
|
51
|
+
@@logger = logger
|
51
52
|
end
|
52
53
|
|
53
54
|
# Set a callback that will be called every time the underlying client emits statistics.
|
@@ -179,7 +180,8 @@ module Rdkafka
|
|
179
180
|
# Set callback to receive delivery reports on config
|
180
181
|
Rdkafka::Bindings.rd_kafka_conf_set_dr_msg_cb(config, Rdkafka::Callbacks::DeliveryCallbackFunction)
|
181
182
|
# Return producer with Kafka client
|
182
|
-
|
183
|
+
partitioner_name = self[:partitioner] || self["partitioner"]
|
184
|
+
Rdkafka::Producer.new(Rdkafka::NativeKafka.new(native_kafka(config, :rd_kafka_producer)), partitioner_name).tap do |producer|
|
183
185
|
opaque.producer = producer
|
184
186
|
end
|
185
187
|
end
|
@@ -194,7 +196,7 @@ module Rdkafka
|
|
194
196
|
opaque = Opaque.new
|
195
197
|
config = native_config(opaque)
|
196
198
|
Rdkafka::Bindings.rd_kafka_conf_set_background_event_cb(config, Rdkafka::Callbacks::BackgroundEventCallbackFunction)
|
197
|
-
Rdkafka::Admin.new(native_kafka(config, :rd_kafka_producer))
|
199
|
+
Rdkafka::Admin.new(Rdkafka::NativeKafka.new(native_kafka(config, :rd_kafka_producer)))
|
198
200
|
end
|
199
201
|
|
200
202
|
# Error that is returned by the underlying rdkafka error if an invalid configuration option is present.
|
@@ -210,7 +212,7 @@ module Rdkafka
|
|
210
212
|
|
211
213
|
# This method is only intended to be used to create a client,
|
212
214
|
# using it in another way will leak memory.
|
213
|
-
def native_config(opaque=nil)
|
215
|
+
def native_config(opaque = nil)
|
214
216
|
Rdkafka::Bindings.rd_kafka_conf_new.tap do |config|
|
215
217
|
# Create config
|
216
218
|
@config_hash.merge(REQUIRED_CONFIG).each do |key, value|
|
@@ -278,8 +280,8 @@ module Rdkafka
|
|
278
280
|
attr_accessor :producer
|
279
281
|
attr_accessor :consumer_rebalance_listener
|
280
282
|
|
281
|
-
def call_delivery_callback(delivery_handle)
|
282
|
-
producer.call_delivery_callback(delivery_handle) if producer
|
283
|
+
def call_delivery_callback(delivery_report, delivery_handle)
|
284
|
+
producer.call_delivery_callback(delivery_report, delivery_handle) if producer
|
283
285
|
end
|
284
286
|
|
285
287
|
def call_on_partitions_assigned(consumer, list)
|
data/lib/rdkafka/consumer.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rdkafka
|
2
4
|
# A consumer of Kafka messages. It uses the high-level consumer approach where the Kafka
|
3
5
|
# brokers automatically assign partitions and load balance partitions over consumers that
|
@@ -14,20 +16,27 @@ module Rdkafka
|
|
14
16
|
# @private
|
15
17
|
def initialize(native_kafka)
|
16
18
|
@native_kafka = native_kafka
|
17
|
-
|
19
|
+
end
|
20
|
+
|
21
|
+
def finalizer
|
22
|
+
->(_) { close }
|
18
23
|
end
|
19
24
|
|
20
25
|
# Close this consumer
|
21
26
|
# @return [nil]
|
22
27
|
def close
|
23
|
-
return
|
28
|
+
return if closed?
|
24
29
|
|
25
|
-
@closing = true
|
26
30
|
Rdkafka::Bindings.rd_kafka_consumer_close(@native_kafka)
|
27
31
|
Rdkafka::Bindings.rd_kafka_destroy(@native_kafka)
|
28
32
|
@native_kafka = nil
|
29
33
|
end
|
30
34
|
|
35
|
+
# Whether this consumer has closed
|
36
|
+
def closed?
|
37
|
+
@native_kafka.nil?
|
38
|
+
end
|
39
|
+
|
31
40
|
# Subscribe to one or more topics letting Kafka handle partition assignments.
|
32
41
|
#
|
33
42
|
# @param topics [Array<String>] One or more topic names
|
@@ -459,7 +468,7 @@ module Rdkafka
|
|
459
468
|
if message
|
460
469
|
yield(message)
|
461
470
|
else
|
462
|
-
if
|
471
|
+
if closed?
|
463
472
|
break
|
464
473
|
else
|
465
474
|
next
|
@@ -468,10 +477,6 @@ module Rdkafka
|
|
468
477
|
end
|
469
478
|
end
|
470
479
|
|
471
|
-
def closed_consumer_check(method)
|
472
|
-
raise Rdkafka::ClosedConsumerError.new(method) if @native_kafka.nil?
|
473
|
-
end
|
474
|
-
|
475
480
|
# Poll for new messages and yield them in batches that may contain
|
476
481
|
# messages from more than one partition.
|
477
482
|
#
|
@@ -527,7 +532,7 @@ module Rdkafka
|
|
527
532
|
bytes = 0
|
528
533
|
end_time = monotonic_now + timeout_ms / 1000.0
|
529
534
|
loop do
|
530
|
-
break if
|
535
|
+
break if closed?
|
531
536
|
max_wait = end_time - monotonic_now
|
532
537
|
max_wait_ms = if max_wait <= 0
|
533
538
|
0 # should not block, but may retrieve a message
|
@@ -561,5 +566,9 @@ module Rdkafka
|
|
561
566
|
# needed because Time.now can go backwards
|
562
567
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
563
568
|
end
|
569
|
+
|
570
|
+
def closed_consumer_check(method)
|
571
|
+
raise Rdkafka::ClosedConsumerError.new(method) if closed?
|
572
|
+
end
|
564
573
|
end
|
565
574
|
end
|
data/lib/rdkafka/error.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rdkafka
|
2
4
|
# Base error class.
|
3
5
|
class BaseError < RuntimeError; end
|
@@ -83,4 +85,11 @@ module Rdkafka
|
|
83
85
|
super("Illegal call to #{method.to_s} on a closed producer")
|
84
86
|
end
|
85
87
|
end
|
88
|
+
|
89
|
+
# Error class for public consumer method calls on a closed admin.
|
90
|
+
class ClosedAdminError < BaseError
|
91
|
+
def initialize(method)
|
92
|
+
super("Illegal call to #{method.to_s} on a closed admin")
|
93
|
+
end
|
94
|
+
end
|
86
95
|
end
|
data/lib/rdkafka/metadata.rb
CHANGED
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rdkafka
|
4
|
+
# @private
|
5
|
+
# A wrapper around a native kafka that polls and cleanly exits
|
6
|
+
class NativeKafka
|
7
|
+
def initialize(inner)
|
8
|
+
@inner = inner
|
9
|
+
|
10
|
+
# Start thread to poll client for delivery callbacks
|
11
|
+
@polling_thread = Thread.new do
|
12
|
+
loop do
|
13
|
+
Rdkafka::Bindings.rd_kafka_poll(inner, 250)
|
14
|
+
# Exit thread if closing and the poll queue is empty
|
15
|
+
if Thread.current[:closing] && Rdkafka::Bindings.rd_kafka_outq_len(inner) == 0
|
16
|
+
break
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
@polling_thread.abort_on_exception = true
|
21
|
+
@polling_thread[:closing] = false
|
22
|
+
end
|
23
|
+
|
24
|
+
def inner
|
25
|
+
@inner
|
26
|
+
end
|
27
|
+
|
28
|
+
def finalizer
|
29
|
+
->(_) { close }
|
30
|
+
end
|
31
|
+
|
32
|
+
def closed?
|
33
|
+
@inner.nil?
|
34
|
+
end
|
35
|
+
|
36
|
+
def close(object_id=nil)
|
37
|
+
return if closed?
|
38
|
+
|
39
|
+
# Flush outstanding activity
|
40
|
+
Rdkafka::Bindings.rd_kafka_flush(@inner, 30 * 1000)
|
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
|
46
|
+
|
47
|
+
Rdkafka::Bindings.rd_kafka_destroy(@inner)
|
48
|
+
|
49
|
+
@inner = nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rdkafka
|
2
4
|
class Producer
|
3
5
|
# Handle to wait for a delivery report which is returned when
|
@@ -6,7 +8,8 @@ module Rdkafka
|
|
6
8
|
layout :pending, :bool,
|
7
9
|
:response, :int,
|
8
10
|
:partition, :int,
|
9
|
-
:offset, :int64
|
11
|
+
:offset, :int64,
|
12
|
+
:topic_name, :pointer
|
10
13
|
|
11
14
|
# @return [String] the name of the operation (e.g. "delivery")
|
12
15
|
def operation_name
|
@@ -15,7 +18,7 @@ module Rdkafka
|
|
15
18
|
|
16
19
|
# @return [DeliveryReport] a report on the delivery of the message
|
17
20
|
def create_result
|
18
|
-
DeliveryReport.new(self[:partition], self[:offset])
|
21
|
+
DeliveryReport.new(self[:partition], self[:offset], self[:topic_name].read_string)
|
19
22
|
end
|
20
23
|
end
|
21
24
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rdkafka
|
2
4
|
class Producer
|
3
5
|
# Delivery report for a successfully produced message.
|
@@ -10,15 +12,20 @@ module Rdkafka
|
|
10
12
|
# @return [Integer]
|
11
13
|
attr_reader :offset
|
12
14
|
|
13
|
-
#
|
15
|
+
# The name of the topic this message was produced to.
|
14
16
|
# @return [String]
|
17
|
+
attr_reader :topic_name
|
18
|
+
|
19
|
+
# Error in case happen during produce.
|
20
|
+
# @return [Integer]
|
15
21
|
attr_reader :error
|
16
22
|
|
17
23
|
private
|
18
24
|
|
19
|
-
def initialize(partition, offset, error = nil)
|
25
|
+
def initialize(partition, offset, topic_name = nil, error = nil)
|
20
26
|
@partition = partition
|
21
27
|
@offset = offset
|
28
|
+
@topic_name = topic_name
|
22
29
|
@error = error
|
23
30
|
end
|
24
31
|
end
|
data/lib/rdkafka/producer.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "objspace"
|
2
4
|
|
3
5
|
module Rdkafka
|
@@ -10,16 +12,22 @@ module Rdkafka
|
|
10
12
|
attr_reader :delivery_callback
|
11
13
|
|
12
14
|
# @private
|
13
|
-
|
14
|
-
|
15
|
+
# Returns the number of arguments accepted by the callback, by default this is nil.
|
16
|
+
#
|
17
|
+
# @return [Integer, nil]
|
18
|
+
attr_reader :delivery_callback_arity
|
19
|
+
|
20
|
+
# @private
|
21
|
+
def initialize(native_kafka, partitioner_name)
|
22
|
+
@native_kafka = native_kafka
|
15
23
|
@partitioner_name = partitioner_name || "consistent_random"
|
16
24
|
|
17
|
-
# Makes sure, that
|
18
|
-
ObjectSpace.define_finalizer(self,
|
25
|
+
# Makes sure, that native kafka gets closed before it gets GCed by Ruby
|
26
|
+
ObjectSpace.define_finalizer(self, native_kafka.finalizer)
|
19
27
|
end
|
20
28
|
|
21
29
|
# Set a callback that will be called every time a message is successfully produced.
|
22
|
-
# The callback is called with a {DeliveryReport}
|
30
|
+
# The callback is called with a {DeliveryReport} and {DeliveryHandle}
|
23
31
|
#
|
24
32
|
# @param callback [Proc, #call] The callback
|
25
33
|
#
|
@@ -27,13 +35,14 @@ module Rdkafka
|
|
27
35
|
def delivery_callback=(callback)
|
28
36
|
raise TypeError.new("Callback has to be callable") unless callback.respond_to?(:call)
|
29
37
|
@delivery_callback = callback
|
38
|
+
@delivery_callback_arity = arity(callback)
|
30
39
|
end
|
31
40
|
|
32
41
|
# Close this producer and wait for the internal poll queue to empty.
|
33
42
|
def close
|
34
43
|
ObjectSpace.undefine_finalizer(self)
|
35
44
|
|
36
|
-
@
|
45
|
+
@native_kafka.close
|
37
46
|
end
|
38
47
|
|
39
48
|
# Partition count for a given topic.
|
@@ -42,10 +51,9 @@ module Rdkafka
|
|
42
51
|
# @param topic [String] The topic name.
|
43
52
|
#
|
44
53
|
# @return partition count [Integer,nil]
|
45
|
-
#
|
46
54
|
def partition_count(topic)
|
47
55
|
closed_producer_check(__method__)
|
48
|
-
Rdkafka::Metadata.new(@
|
56
|
+
Rdkafka::Metadata.new(@native_kafka.inner, topic).topics&.first[:partition_count]
|
49
57
|
end
|
50
58
|
|
51
59
|
# 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.
|
@@ -137,7 +145,7 @@ module Rdkafka
|
|
137
145
|
|
138
146
|
# Produce the message
|
139
147
|
response = Rdkafka::Bindings.rd_kafka_producev(
|
140
|
-
@
|
148
|
+
@native_kafka.inner,
|
141
149
|
*args
|
142
150
|
)
|
143
151
|
|
@@ -150,13 +158,22 @@ module Rdkafka
|
|
150
158
|
delivery_handle
|
151
159
|
end
|
152
160
|
|
153
|
-
|
154
|
-
|
155
|
-
|
161
|
+
def call_delivery_callback(delivery_report, delivery_handle)
|
162
|
+
return unless @delivery_callback
|
163
|
+
|
164
|
+
args = [delivery_report, delivery_handle].take(@delivery_callback_arity)
|
165
|
+
@delivery_callback.call(*args)
|
166
|
+
end
|
167
|
+
|
168
|
+
def arity(callback)
|
169
|
+
return callback.arity if callback.respond_to?(:arity)
|
170
|
+
|
171
|
+
callback.method(:call).arity
|
156
172
|
end
|
157
173
|
|
174
|
+
private
|
158
175
|
def closed_producer_check(method)
|
159
|
-
raise Rdkafka::ClosedProducerError.new(method) if @
|
176
|
+
raise Rdkafka::ClosedProducerError.new(method) if @native_kafka.closed?
|
160
177
|
end
|
161
178
|
end
|
162
179
|
end
|
data/lib/rdkafka/version.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rdkafka
|
2
|
-
VERSION = "0.
|
3
|
-
LIBRDKAFKA_VERSION = "1.9.
|
4
|
-
LIBRDKAFKA_SOURCE_SHA256 = "
|
4
|
+
VERSION = "0.13.0.beta.1"
|
5
|
+
LIBRDKAFKA_VERSION = "1.9.2"
|
6
|
+
LIBRDKAFKA_SOURCE_SHA256 = "3fba157a9f80a0889c982acdd44608be8a46142270a389008b22d921be1198ad"
|
5
7
|
end
|
data/lib/rdkafka.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "rdkafka/version"
|
2
4
|
|
3
5
|
require "rdkafka/abstract_handle"
|
@@ -16,7 +18,7 @@ require "rdkafka/consumer/partition"
|
|
16
18
|
require "rdkafka/consumer/topic_partition_list"
|
17
19
|
require "rdkafka/error"
|
18
20
|
require "rdkafka/metadata"
|
21
|
+
require "rdkafka/native_kafka"
|
19
22
|
require "rdkafka/producer"
|
20
|
-
require "rdkafka/producer/client"
|
21
23
|
require "rdkafka/producer/delivery_handle"
|
22
24
|
require "rdkafka/producer/delivery_report"
|
data/rdkafka.gemspec
CHANGED
data/spec/rdkafka/admin_spec.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
require "ostruct"
|
3
5
|
|
4
6
|
describe Rdkafka::Admin do
|
5
|
-
let(:config)
|
6
|
-
let(:admin)
|
7
|
+
let(:config) { rdkafka_config }
|
8
|
+
let(:admin) { config.admin }
|
7
9
|
|
8
10
|
after do
|
9
11
|
# Registry should always end up being empty
|
@@ -174,7 +176,6 @@ describe Rdkafka::Admin do
|
|
174
176
|
end
|
175
177
|
end
|
176
178
|
|
177
|
-
|
178
179
|
it "deletes a topic that was newly created" do
|
179
180
|
create_topic_handle = admin.create_topic(topic_name, topic_partition_count, topic_replication_factor)
|
180
181
|
create_topic_report = create_topic_handle.wait(max_wait_timeout: 15.0)
|
data/spec/rdkafka/config_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
|
3
5
|
describe Rdkafka::Config do
|
@@ -148,6 +150,18 @@ describe Rdkafka::Config do
|
|
148
150
|
}.to raise_error(Rdkafka::Config::ConfigError, "No such configuration property: \"invalid.key\"")
|
149
151
|
end
|
150
152
|
|
153
|
+
it "allows string partitioner key" do
|
154
|
+
expect(Rdkafka::Producer).to receive(:new).with(kind_of(Rdkafka::NativeKafka), "murmur2")
|
155
|
+
config = Rdkafka::Config.new("partitioner" => "murmur2")
|
156
|
+
config.producer
|
157
|
+
end
|
158
|
+
|
159
|
+
it "allows symbol partitioner key" do
|
160
|
+
expect(Rdkafka::Producer).to receive(:new).with(kind_of(Rdkafka::NativeKafka), "murmur2")
|
161
|
+
config = Rdkafka::Config.new(:partitioner => "murmur2")
|
162
|
+
config.producer
|
163
|
+
end
|
164
|
+
|
151
165
|
it "should allow configuring zstd compression" do
|
152
166
|
config = Rdkafka::Config.new('compression.codec' => 'zstd')
|
153
167
|
begin
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
require "ostruct"
|
3
5
|
require 'securerandom'
|
@@ -1005,4 +1007,12 @@ describe Rdkafka::Consumer do
|
|
1005
1007
|
end
|
1006
1008
|
end
|
1007
1009
|
end
|
1010
|
+
|
1011
|
+
it "provides a finalizer that closes the native kafka client" do
|
1012
|
+
expect(consumer.closed?).to eq(false)
|
1013
|
+
|
1014
|
+
consumer.finalizer.call("some-ignored-object-id")
|
1015
|
+
|
1016
|
+
expect(consumer.closed?).to eq(true)
|
1017
|
+
end
|
1008
1018
|
end
|
data/spec/rdkafka/error_spec.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
|
3
|
-
describe Rdkafka::
|
5
|
+
describe Rdkafka::NativeKafka do
|
4
6
|
let(:config) { rdkafka_producer_config }
|
5
7
|
let(:native) { config.send(:native_kafka, config.send(:native_config), :rd_kafka_producer) }
|
6
8
|
let(:closing) { false }
|
@@ -63,8 +65,8 @@ describe Rdkafka::Producer::Client do
|
|
63
65
|
client
|
64
66
|
end
|
65
67
|
|
66
|
-
it "exposes
|
67
|
-
expect(client.
|
68
|
+
it "exposes inner client" do
|
69
|
+
expect(client.inner).to eq(native)
|
68
70
|
end
|
69
71
|
|
70
72
|
context "when client was not yet closed (`nil`)" do
|
@@ -94,7 +96,7 @@ describe Rdkafka::Producer::Client do
|
|
94
96
|
it "closes and unassign the native client" do
|
95
97
|
client.close
|
96
98
|
|
97
|
-
expect(client.
|
99
|
+
expect(client.inner).to eq(nil)
|
98
100
|
expect(client.closed?).to eq(true)
|
99
101
|
end
|
100
102
|
end
|
@@ -129,13 +131,13 @@ describe Rdkafka::Producer::Client do
|
|
129
131
|
it "does not close and unassign the native client again" do
|
130
132
|
client.close
|
131
133
|
|
132
|
-
expect(client.
|
134
|
+
expect(client.inner).to eq(nil)
|
133
135
|
expect(client.closed?).to eq(true)
|
134
136
|
end
|
135
137
|
end
|
136
138
|
end
|
137
139
|
|
138
|
-
it "
|
140
|
+
it "provides a finalizer that closes the native kafka client" do
|
139
141
|
expect(client.closed?).to eq(false)
|
140
142
|
|
141
143
|
client.finalizer.call("some-ignored-object-id")
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
|
3
5
|
describe Rdkafka::Producer::DeliveryHandle do
|
@@ -9,6 +11,7 @@ describe Rdkafka::Producer::DeliveryHandle do
|
|
9
11
|
handle[:response] = response
|
10
12
|
handle[:partition] = 2
|
11
13
|
handle[:offset] = 100
|
14
|
+
handle[:topic_name] = FFI::MemoryPointer.from_string("produce_test_topic")
|
12
15
|
end
|
13
16
|
end
|
14
17
|
|
@@ -29,6 +32,7 @@ describe Rdkafka::Producer::DeliveryHandle do
|
|
29
32
|
|
30
33
|
expect(report.partition).to eq(2)
|
31
34
|
expect(report.offset).to eq(100)
|
35
|
+
expect(report.topic_name).to eq("produce_test_topic")
|
32
36
|
end
|
33
37
|
|
34
38
|
it "should wait without a timeout" do
|
@@ -36,6 +40,7 @@ describe Rdkafka::Producer::DeliveryHandle do
|
|
36
40
|
|
37
41
|
expect(report.partition).to eq(2)
|
38
42
|
expect(report.offset).to eq(100)
|
43
|
+
expect(report.topic_name).to eq("produce_test_topic")
|
39
44
|
end
|
40
45
|
end
|
41
46
|
end
|
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
|
3
5
|
describe Rdkafka::Producer::DeliveryReport do
|
4
|
-
subject { Rdkafka::Producer::DeliveryReport.new(2, 100, "
|
6
|
+
subject { Rdkafka::Producer::DeliveryReport.new(2, 100, "topic", -1) }
|
5
7
|
|
6
8
|
it "should get the partition" do
|
7
9
|
expect(subject.partition).to eq 2
|
@@ -11,7 +13,11 @@ describe Rdkafka::Producer::DeliveryReport do
|
|
11
13
|
expect(subject.offset).to eq 100
|
12
14
|
end
|
13
15
|
|
16
|
+
it "should get the topic_name" do
|
17
|
+
expect(subject.topic_name).to eq "topic"
|
18
|
+
end
|
19
|
+
|
14
20
|
it "should get the error" do
|
15
|
-
expect(subject.error).to eq
|
21
|
+
expect(subject.error).to eq -1
|
16
22
|
end
|
17
23
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
require "zlib"
|
3
5
|
|
@@ -7,7 +9,7 @@ describe Rdkafka::Producer do
|
|
7
9
|
|
8
10
|
after do
|
9
11
|
# Registry should always end up being empty
|
10
|
-
expect(Rdkafka::Producer::DeliveryHandle::REGISTRY).to
|
12
|
+
expect(Rdkafka::Producer::DeliveryHandle::REGISTRY).to eq({})
|
11
13
|
producer.close
|
12
14
|
consumer.close
|
13
15
|
end
|
@@ -30,6 +32,7 @@ describe Rdkafka::Producer do
|
|
30
32
|
expect(report).not_to be_nil
|
31
33
|
expect(report.partition).to eq 1
|
32
34
|
expect(report.offset).to be >= 0
|
35
|
+
expect(report.topic_name).to eq "produce_test_topic"
|
33
36
|
@callback_called = true
|
34
37
|
end
|
35
38
|
|
@@ -49,6 +52,27 @@ describe Rdkafka::Producer do
|
|
49
52
|
# Callback should have been called
|
50
53
|
expect(@callback_called).to be true
|
51
54
|
end
|
55
|
+
|
56
|
+
it "should provide handle" do
|
57
|
+
@callback_handle = nil
|
58
|
+
|
59
|
+
producer.delivery_callback = lambda { |_, handle| @callback_handle = handle }
|
60
|
+
|
61
|
+
# Produce a message
|
62
|
+
handle = producer.produce(
|
63
|
+
topic: "produce_test_topic",
|
64
|
+
payload: "payload",
|
65
|
+
key: "key"
|
66
|
+
)
|
67
|
+
|
68
|
+
# Wait for it to be delivered
|
69
|
+
handle.wait(max_wait_timeout: 15)
|
70
|
+
|
71
|
+
# Join the producer thread.
|
72
|
+
producer.close
|
73
|
+
|
74
|
+
expect(handle).to be @callback_handle
|
75
|
+
end
|
52
76
|
end
|
53
77
|
|
54
78
|
context "with a callable object" do
|
@@ -92,6 +116,37 @@ describe Rdkafka::Producer do
|
|
92
116
|
expect(called_report.first).not_to be_nil
|
93
117
|
expect(called_report.first.partition).to eq 1
|
94
118
|
expect(called_report.first.offset).to be >= 0
|
119
|
+
expect(called_report.first.topic_name).to eq "produce_test_topic"
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should provide handle" do
|
123
|
+
callback_handles = []
|
124
|
+
callback = Class.new do
|
125
|
+
def initialize(callback_handles)
|
126
|
+
@callback_handles = callback_handles
|
127
|
+
end
|
128
|
+
|
129
|
+
def call(_, handle)
|
130
|
+
@callback_handles << handle
|
131
|
+
end
|
132
|
+
end
|
133
|
+
producer.delivery_callback = callback.new(callback_handles)
|
134
|
+
|
135
|
+
# Produce a message
|
136
|
+
handle = producer.produce(
|
137
|
+
topic: "produce_test_topic",
|
138
|
+
payload: "payload",
|
139
|
+
key: "key"
|
140
|
+
)
|
141
|
+
|
142
|
+
# Wait for it to be delivered
|
143
|
+
handle.wait(max_wait_timeout: 15)
|
144
|
+
|
145
|
+
# Join the producer thread.
|
146
|
+
producer.close
|
147
|
+
|
148
|
+
# Callback should have been called
|
149
|
+
expect(handle).to be callback_handles.first
|
95
150
|
end
|
96
151
|
end
|
97
152
|
|
@@ -200,6 +255,28 @@ describe Rdkafka::Producer do
|
|
200
255
|
expect(messages[2].key).to eq key
|
201
256
|
end
|
202
257
|
|
258
|
+
it "should produce a message with empty string without crashing" do
|
259
|
+
messages = [{key: 'a', partition_key: ''}]
|
260
|
+
|
261
|
+
messages = messages.map do |m|
|
262
|
+
handle = producer.produce(
|
263
|
+
topic: "partitioner_test_topic",
|
264
|
+
payload: "payload partition",
|
265
|
+
key: m[:key],
|
266
|
+
partition_key: m[:partition_key]
|
267
|
+
)
|
268
|
+
report = handle.wait(max_wait_timeout: 5)
|
269
|
+
|
270
|
+
wait_for_message(
|
271
|
+
topic: "partitioner_test_topic",
|
272
|
+
delivery_report: report,
|
273
|
+
)
|
274
|
+
end
|
275
|
+
|
276
|
+
expect(messages[0].partition).to eq 0
|
277
|
+
expect(messages[0].key).to eq 'a'
|
278
|
+
end
|
279
|
+
|
203
280
|
it "should produce a message with utf-8 encoding" do
|
204
281
|
handle = producer.produce(
|
205
282
|
topic: "produce_test_topic",
|
@@ -397,7 +474,8 @@ describe Rdkafka::Producer do
|
|
397
474
|
|
398
475
|
report_json = JSON.generate(
|
399
476
|
"partition" => report.partition,
|
400
|
-
"offset" => report.offset
|
477
|
+
"offset" => report.offset,
|
478
|
+
"topic_name" => report.topic_name
|
401
479
|
)
|
402
480
|
|
403
481
|
writer.write(report_json)
|
@@ -409,7 +487,8 @@ describe Rdkafka::Producer do
|
|
409
487
|
report_hash = JSON.parse(reader.read)
|
410
488
|
report = Rdkafka::Producer::DeliveryReport.new(
|
411
489
|
report_hash["partition"],
|
412
|
-
report_hash["offset"]
|
490
|
+
report_hash["offset"],
|
491
|
+
report_hash["topic_name"]
|
413
492
|
)
|
414
493
|
|
415
494
|
reader.close
|
data/spec/spec_helper.rb
CHANGED
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.
|
4
|
+
version: 0.13.0.beta.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thijs Cadier
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -176,8 +176,8 @@ files:
|
|
176
176
|
- lib/rdkafka/consumer/topic_partition_list.rb
|
177
177
|
- lib/rdkafka/error.rb
|
178
178
|
- lib/rdkafka/metadata.rb
|
179
|
+
- lib/rdkafka/native_kafka.rb
|
179
180
|
- lib/rdkafka/producer.rb
|
180
|
-
- lib/rdkafka/producer/client.rb
|
181
181
|
- lib/rdkafka/producer/delivery_handle.rb
|
182
182
|
- lib/rdkafka/producer/delivery_report.rb
|
183
183
|
- lib/rdkafka/version.rb
|
@@ -197,7 +197,7 @@ files:
|
|
197
197
|
- spec/rdkafka/consumer_spec.rb
|
198
198
|
- spec/rdkafka/error_spec.rb
|
199
199
|
- spec/rdkafka/metadata_spec.rb
|
200
|
-
- spec/rdkafka/
|
200
|
+
- spec/rdkafka/native_kafka_spec.rb
|
201
201
|
- spec/rdkafka/producer/delivery_handle_spec.rb
|
202
202
|
- spec/rdkafka/producer/delivery_report_spec.rb
|
203
203
|
- spec/rdkafka/producer_spec.rb
|
@@ -206,7 +206,7 @@ homepage: https://github.com/thijsc/rdkafka-ruby
|
|
206
206
|
licenses:
|
207
207
|
- MIT
|
208
208
|
metadata: {}
|
209
|
-
post_install_message:
|
209
|
+
post_install_message:
|
210
210
|
rdoc_options: []
|
211
211
|
require_paths:
|
212
212
|
- lib
|
@@ -221,8 +221,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
221
221
|
- !ruby/object:Gem::Version
|
222
222
|
version: 1.3.1
|
223
223
|
requirements: []
|
224
|
-
rubygems_version: 3.
|
225
|
-
signing_key:
|
224
|
+
rubygems_version: 3.3.7
|
225
|
+
signing_key:
|
226
226
|
specification_version: 4
|
227
227
|
summary: The rdkafka gem is a modern Kafka client library for Ruby based on librdkafka.
|
228
228
|
It wraps the production-ready C client using the ffi gem and targets Kafka 1.0+
|
@@ -243,7 +243,7 @@ test_files:
|
|
243
243
|
- spec/rdkafka/consumer_spec.rb
|
244
244
|
- spec/rdkafka/error_spec.rb
|
245
245
|
- spec/rdkafka/metadata_spec.rb
|
246
|
-
- spec/rdkafka/
|
246
|
+
- spec/rdkafka/native_kafka_spec.rb
|
247
247
|
- spec/rdkafka/producer/delivery_handle_spec.rb
|
248
248
|
- spec/rdkafka/producer/delivery_report_spec.rb
|
249
249
|
- spec/rdkafka/producer_spec.rb
|
@@ -1,47 +0,0 @@
|
|
1
|
-
module Rdkafka
|
2
|
-
class Producer
|
3
|
-
class Client
|
4
|
-
def initialize(native)
|
5
|
-
@native = native
|
6
|
-
|
7
|
-
# Start thread to poll client for delivery callbacks
|
8
|
-
@polling_thread = Thread.new do
|
9
|
-
loop do
|
10
|
-
Rdkafka::Bindings.rd_kafka_poll(native, 250)
|
11
|
-
# Exit thread if closing and the poll queue is empty
|
12
|
-
if Thread.current[:closing] && Rdkafka::Bindings.rd_kafka_outq_len(native) == 0
|
13
|
-
break
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
@polling_thread.abort_on_exception = true
|
18
|
-
@polling_thread[:closing] = false
|
19
|
-
end
|
20
|
-
|
21
|
-
def native
|
22
|
-
@native
|
23
|
-
end
|
24
|
-
|
25
|
-
def finalizer
|
26
|
-
->(_) { close }
|
27
|
-
end
|
28
|
-
|
29
|
-
def closed?
|
30
|
-
@native.nil?
|
31
|
-
end
|
32
|
-
|
33
|
-
def close(object_id=nil)
|
34
|
-
return unless @native
|
35
|
-
|
36
|
-
# Indicate to polling thread that we're closing
|
37
|
-
@polling_thread[:closing] = true
|
38
|
-
# Wait for the polling thread to finish up
|
39
|
-
@polling_thread.join
|
40
|
-
|
41
|
-
Rdkafka::Bindings.rd_kafka_destroy(@native)
|
42
|
-
|
43
|
-
@native = nil
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|