rdkafka 0.16.0.beta1 → 0.16.0
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
- checksums.yaml.gz.sig +0 -0
- data/.github/workflows/ci.yml +1 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +9 -1
- data/README.md +1 -0
- data/lib/rdkafka/admin/config_binding_result.rb +30 -0
- data/lib/rdkafka/admin/config_resource_binding_result.rb +18 -0
- data/lib/rdkafka/admin/describe_acl_report.rb +1 -0
- data/lib/rdkafka/admin/describe_configs_handle.rb +33 -0
- data/lib/rdkafka/admin/describe_configs_report.rb +54 -0
- data/lib/rdkafka/admin/incremental_alter_configs_handle.rb +33 -0
- data/lib/rdkafka/admin/incremental_alter_configs_report.rb +54 -0
- data/lib/rdkafka/admin.rb +204 -0
- data/lib/rdkafka/bindings.rb +51 -0
- data/lib/rdkafka/callbacks.rb +85 -9
- data/lib/rdkafka/consumer/headers.rb +3 -13
- data/lib/rdkafka/producer/delivery_handle.rb +5 -1
- data/lib/rdkafka/producer.rb +96 -5
- data/lib/rdkafka/version.rb +1 -1
- data/lib/rdkafka.rb +6 -0
- data/rdkafka.gemspec +1 -1
- data/spec/rdkafka/admin_spec.rb +283 -3
- data/spec/rdkafka/consumer/headers_spec.rb +2 -5
- data/spec/rdkafka/producer/delivery_handle_spec.rb +1 -1
- data/spec/rdkafka/producer_spec.rb +42 -0
- data.tar.gz.sig +0 -0
- metadata +10 -4
- metadata.gz.sig +0 -0
data/lib/rdkafka/callbacks.rb
CHANGED
@@ -113,6 +113,42 @@ module Rdkafka
|
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
116
|
+
class DescribeConfigsResult
|
117
|
+
attr_reader :result_error, :error_string, :results, :results_count
|
118
|
+
|
119
|
+
def initialize(event_ptr)
|
120
|
+
@results=[]
|
121
|
+
@result_error = Rdkafka::Bindings.rd_kafka_event_error(event_ptr)
|
122
|
+
@error_string = Rdkafka::Bindings.rd_kafka_event_error_string(event_ptr)
|
123
|
+
|
124
|
+
if @result_error == 0
|
125
|
+
configs_describe_result = Rdkafka::Bindings.rd_kafka_event_DescribeConfigs_result(event_ptr)
|
126
|
+
# Get the number of matching acls
|
127
|
+
pointer_to_size_t = FFI::MemoryPointer.new(:int32)
|
128
|
+
@results = Rdkafka::Bindings.rd_kafka_DescribeConfigs_result_resources(configs_describe_result, pointer_to_size_t)
|
129
|
+
@results_count = pointer_to_size_t.read_int
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class IncrementalAlterConfigsResult
|
135
|
+
attr_reader :result_error, :error_string, :results, :results_count
|
136
|
+
|
137
|
+
def initialize(event_ptr)
|
138
|
+
@results=[]
|
139
|
+
@result_error = Rdkafka::Bindings.rd_kafka_event_error(event_ptr)
|
140
|
+
@error_string = Rdkafka::Bindings.rd_kafka_event_error_string(event_ptr)
|
141
|
+
|
142
|
+
if @result_error == 0
|
143
|
+
incremental_alter_result = Rdkafka::Bindings.rd_kafka_event_IncrementalAlterConfigs_result(event_ptr)
|
144
|
+
# Get the number of matching acls
|
145
|
+
pointer_to_size_t = FFI::MemoryPointer.new(:int32)
|
146
|
+
@results = Rdkafka::Bindings.rd_kafka_IncrementalAlterConfigs_result_resources(incremental_alter_result, pointer_to_size_t)
|
147
|
+
@results_count = pointer_to_size_t.read_int
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
116
152
|
# FFI Function used for Create Topic and Delete Topic callbacks
|
117
153
|
BackgroundEventCallbackFunction = FFI::Function.new(
|
118
154
|
:void, [:pointer, :pointer, :pointer]
|
@@ -123,20 +159,24 @@ module Rdkafka
|
|
123
159
|
# @private
|
124
160
|
class BackgroundEventCallback
|
125
161
|
def self.call(_, event_ptr, _)
|
126
|
-
|
127
|
-
|
162
|
+
case Rdkafka::Bindings.rd_kafka_event_type(event_ptr)
|
163
|
+
when Rdkafka::Bindings::RD_KAFKA_EVENT_CREATETOPICS_RESULT
|
128
164
|
process_create_topic(event_ptr)
|
129
|
-
|
165
|
+
when Rdkafka::Bindings::RD_KAFKA_EVENT_DESCRIBECONFIGS_RESULT
|
166
|
+
process_describe_configs(event_ptr)
|
167
|
+
when Rdkafka::Bindings::RD_KAFKA_EVENT_INCREMENTALALTERCONFIGS_RESULT
|
168
|
+
process_incremental_alter_configs(event_ptr)
|
169
|
+
when Rdkafka::Bindings::RD_KAFKA_EVENT_DELETETOPICS_RESULT
|
130
170
|
process_delete_topic(event_ptr)
|
131
|
-
|
171
|
+
when Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_CREATEPARTITIONS_RESULT
|
132
172
|
process_create_partitions(event_ptr)
|
133
|
-
|
173
|
+
when Rdkafka::Bindings::RD_KAFKA_EVENT_CREATEACLS_RESULT
|
134
174
|
process_create_acl(event_ptr)
|
135
|
-
|
175
|
+
when Rdkafka::Bindings::RD_KAFKA_EVENT_DELETEACLS_RESULT
|
136
176
|
process_delete_acl(event_ptr)
|
137
|
-
|
177
|
+
when Rdkafka::Bindings::RD_KAFKA_EVENT_DESCRIBEACLS_RESULT
|
138
178
|
process_describe_acl(event_ptr)
|
139
|
-
|
179
|
+
when Rdkafka::Bindings::RD_KAFKA_EVENT_DELETEGROUPS_RESULT
|
140
180
|
process_delete_groups(event_ptr)
|
141
181
|
end
|
142
182
|
end
|
@@ -161,6 +201,42 @@ module Rdkafka
|
|
161
201
|
end
|
162
202
|
end
|
163
203
|
|
204
|
+
def self.process_describe_configs(event_ptr)
|
205
|
+
describe_configs = DescribeConfigsResult.new(event_ptr)
|
206
|
+
describe_configs_handle_ptr = Rdkafka::Bindings.rd_kafka_event_opaque(event_ptr)
|
207
|
+
|
208
|
+
if describe_configs_handle = Rdkafka::Admin::DescribeConfigsHandle.remove(describe_configs_handle_ptr.address)
|
209
|
+
describe_configs_handle[:response] = describe_configs.result_error
|
210
|
+
describe_configs_handle[:response_string] = describe_configs.error_string
|
211
|
+
describe_configs_handle[:pending] = false
|
212
|
+
|
213
|
+
if describe_configs.result_error == 0
|
214
|
+
describe_configs_handle[:config_entries] = describe_configs.results
|
215
|
+
describe_configs_handle[:entry_count] = describe_configs.results_count
|
216
|
+
end
|
217
|
+
|
218
|
+
describe_configs_handle.unlock
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def self.process_incremental_alter_configs(event_ptr)
|
223
|
+
incremental_alter = IncrementalAlterConfigsResult.new(event_ptr)
|
224
|
+
incremental_alter_handle_ptr = Rdkafka::Bindings.rd_kafka_event_opaque(event_ptr)
|
225
|
+
|
226
|
+
if incremental_alter_handle = Rdkafka::Admin::IncrementalAlterConfigsHandle.remove(incremental_alter_handle_ptr.address)
|
227
|
+
incremental_alter_handle[:response] = incremental_alter.result_error
|
228
|
+
incremental_alter_handle[:response_string] = incremental_alter.error_string
|
229
|
+
incremental_alter_handle[:pending] = false
|
230
|
+
|
231
|
+
if incremental_alter.result_error == 0
|
232
|
+
incremental_alter_handle[:config_entries] = incremental_alter.results
|
233
|
+
incremental_alter_handle[:entry_count] = incremental_alter.results_count
|
234
|
+
end
|
235
|
+
|
236
|
+
incremental_alter_handle.unlock
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
164
240
|
def self.process_delete_groups(event_ptr)
|
165
241
|
delete_groups_result = Rdkafka::Bindings.rd_kafka_event_DeleteGroups_result(event_ptr)
|
166
242
|
|
@@ -263,7 +339,7 @@ module Rdkafka
|
|
263
339
|
describe_acl_handle[:response_string] = describe_acl.error_string
|
264
340
|
|
265
341
|
if describe_acl.result_error == 0
|
266
|
-
describe_acl_handle[:acls]
|
342
|
+
describe_acl_handle[:acls] = describe_acl.matching_acls
|
267
343
|
describe_acl_handle[:acls_count] = describe_acl.matching_acls_count
|
268
344
|
end
|
269
345
|
|
@@ -4,17 +4,7 @@ module Rdkafka
|
|
4
4
|
class Consumer
|
5
5
|
# Interface to return headers for a consumer message
|
6
6
|
module Headers
|
7
|
-
|
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
|
7
|
+
EMPTY_HEADERS = {}.freeze
|
18
8
|
|
19
9
|
# Reads a librdkafka native message's headers and returns them as a Ruby Hash
|
20
10
|
#
|
@@ -28,7 +18,7 @@ module Rdkafka
|
|
28
18
|
err = Rdkafka::Bindings.rd_kafka_message_headers(native_message, headers_ptrptr)
|
29
19
|
|
30
20
|
if err == Rdkafka::Bindings::RD_KAFKA_RESP_ERR__NOENT
|
31
|
-
return
|
21
|
+
return EMPTY_HEADERS
|
32
22
|
elsif err != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
33
23
|
raise Rdkafka::RdkafkaError.new(err, "Error reading message headers")
|
34
24
|
end
|
@@ -39,7 +29,7 @@ module Rdkafka
|
|
39
29
|
value_ptrptr = FFI::MemoryPointer.new(:pointer)
|
40
30
|
size_ptr = Rdkafka::Bindings::SizePtr.new
|
41
31
|
|
42
|
-
headers =
|
32
|
+
headers = {}
|
43
33
|
|
44
34
|
idx = 0
|
45
35
|
loop do
|
@@ -14,6 +14,10 @@ module Rdkafka
|
|
14
14
|
# @return [Object, nil] label set during message production or nil by default
|
15
15
|
attr_accessor :label
|
16
16
|
|
17
|
+
# @return [String] topic where we are trying to send the message
|
18
|
+
# We use this instead of reading from `topic_name` pointer to save on memory allocations
|
19
|
+
attr_accessor :topic
|
20
|
+
|
17
21
|
# @return [String] the name of the operation (e.g. "delivery")
|
18
22
|
def operation_name
|
19
23
|
"delivery"
|
@@ -26,7 +30,7 @@ module Rdkafka
|
|
26
30
|
self[:offset],
|
27
31
|
# For part of errors, we will not get a topic name reference and in cases like this
|
28
32
|
# we should not return it
|
29
|
-
|
33
|
+
topic,
|
30
34
|
self[:response] != 0 ? RdkafkaError.new(self[:response]) : nil,
|
31
35
|
label
|
32
36
|
)
|
data/lib/rdkafka/producer.rb
CHANGED
@@ -9,7 +9,15 @@ module Rdkafka
|
|
9
9
|
# Cache partitions count for 30 seconds
|
10
10
|
PARTITIONS_COUNT_TTL = 30
|
11
11
|
|
12
|
-
|
12
|
+
# Empty hash used as a default
|
13
|
+
EMPTY_HASH = {}.freeze
|
14
|
+
|
15
|
+
private_constant :PARTITIONS_COUNT_TTL, :EMPTY_HASH
|
16
|
+
|
17
|
+
# Raised when there was a critical issue when invoking rd_kafka_topic_new
|
18
|
+
# This is a temporary solution until https://github.com/karafka/rdkafka-ruby/issues/451 is
|
19
|
+
# resolved and this is normalized in all the places
|
20
|
+
class TopicHandleCreationError < RuntimeError; end
|
13
21
|
|
14
22
|
# @private
|
15
23
|
# Returns the current delivery callback, by default this is nil.
|
@@ -28,6 +36,8 @@ module Rdkafka
|
|
28
36
|
# @param partitioner_name [String, nil] name of the partitioner we want to use or nil to use
|
29
37
|
# the "consistent_random" default
|
30
38
|
def initialize(native_kafka, partitioner_name)
|
39
|
+
@topics_refs_map = {}
|
40
|
+
@topics_configs = {}
|
31
41
|
@native_kafka = native_kafka
|
32
42
|
@partitioner_name = partitioner_name || "consistent_random"
|
33
43
|
|
@@ -54,6 +64,52 @@ module Rdkafka
|
|
54
64
|
end
|
55
65
|
end
|
56
66
|
|
67
|
+
# Sets alternative set of configuration details that can be set per topic
|
68
|
+
# @note It is not allowed to re-set the same topic config twice because of the underlying
|
69
|
+
# librdkafka caching
|
70
|
+
# @param topic [String] The topic name
|
71
|
+
# @param config [Hash] config we want to use per topic basis
|
72
|
+
# @param config_hash [Integer] hash of the config. We expect it here instead of computing it,
|
73
|
+
# because it is already computed during the retrieval attempt in the `#produce` flow.
|
74
|
+
def set_topic_config(topic, config, config_hash)
|
75
|
+
# Ensure lock on topic reference just in case
|
76
|
+
@native_kafka.with_inner do |inner|
|
77
|
+
@topics_refs_map[topic] ||= {}
|
78
|
+
@topics_configs[topic] ||= {}
|
79
|
+
|
80
|
+
return if @topics_configs[topic].key?(config_hash)
|
81
|
+
|
82
|
+
# If config is empty, we create an empty reference that will be used with defaults
|
83
|
+
rd_topic_config = if config.empty?
|
84
|
+
nil
|
85
|
+
else
|
86
|
+
Rdkafka::Bindings.rd_kafka_topic_conf_new.tap do |topic_config|
|
87
|
+
config.each do |key, value|
|
88
|
+
error_buffer = FFI::MemoryPointer.new(:char, 256)
|
89
|
+
result = Rdkafka::Bindings.rd_kafka_topic_conf_set(
|
90
|
+
topic_config,
|
91
|
+
key.to_s,
|
92
|
+
value.to_s,
|
93
|
+
error_buffer,
|
94
|
+
256
|
95
|
+
)
|
96
|
+
|
97
|
+
unless result == :config_ok
|
98
|
+
raise Config::ConfigError.new(error_buffer.read_string)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
topic_handle = Bindings.rd_kafka_topic_new(inner, topic, rd_topic_config)
|
105
|
+
|
106
|
+
raise TopicHandleCreationError.new("Error creating topic handle for topic #{topic}") if topic_handle.null?
|
107
|
+
|
108
|
+
@topics_configs[topic][config_hash] = config
|
109
|
+
@topics_refs_map[topic][config_hash] = topic_handle
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
57
113
|
# Starts the native Kafka polling thread and kicks off the init polling
|
58
114
|
# @note Not needed to run unless explicit start was disabled
|
59
115
|
def start
|
@@ -83,7 +139,18 @@ module Rdkafka
|
|
83
139
|
def close
|
84
140
|
return if closed?
|
85
141
|
ObjectSpace.undefine_finalizer(self)
|
86
|
-
|
142
|
+
|
143
|
+
@native_kafka.close do
|
144
|
+
# We need to remove the topics references objects before we destroy the producer,
|
145
|
+
# otherwise they would leak out
|
146
|
+
@topics_refs_map.each_value do |refs|
|
147
|
+
refs.each_value do |ref|
|
148
|
+
Rdkafka::Bindings.rd_kafka_topic_destroy(ref)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
@topics_refs_map.clear
|
87
154
|
end
|
88
155
|
|
89
156
|
# Whether this producer has closed
|
@@ -182,11 +249,22 @@ module Rdkafka
|
|
182
249
|
# @param timestamp [Time,Integer,nil] Optional timestamp of this message. Integer timestamp is in milliseconds since Jan 1 1970.
|
183
250
|
# @param headers [Hash<String,String>] Optional message headers
|
184
251
|
# @param label [Object, nil] a label that can be assigned when producing a message that will be part of the delivery handle and the delivery report
|
252
|
+
# @param topic_config [Hash] topic config for given message dispatch. Allows to send messages to topics with different configuration
|
185
253
|
#
|
186
254
|
# @return [DeliveryHandle] Delivery handle that can be used to wait for the result of producing this message
|
187
255
|
#
|
188
256
|
# @raise [RdkafkaError] When adding the message to rdkafka's queue failed
|
189
|
-
def produce(
|
257
|
+
def produce(
|
258
|
+
topic:,
|
259
|
+
payload: nil,
|
260
|
+
key: nil,
|
261
|
+
partition: nil,
|
262
|
+
partition_key: nil,
|
263
|
+
timestamp: nil,
|
264
|
+
headers: nil,
|
265
|
+
label: nil,
|
266
|
+
topic_config: EMPTY_HASH
|
267
|
+
)
|
190
268
|
closed_producer_check(__method__)
|
191
269
|
|
192
270
|
# Start by checking and converting the input
|
@@ -205,8 +283,20 @@ module Rdkafka
|
|
205
283
|
key.bytesize
|
206
284
|
end
|
207
285
|
|
286
|
+
topic_config_hash = topic_config.hash
|
287
|
+
|
288
|
+
# Checks if we have the rdkafka topic reference object ready. It saves us on object
|
289
|
+
# allocation and allows to use custom config on demand.
|
290
|
+
set_topic_config(topic, topic_config, topic_config_hash) unless @topics_refs_map.dig(topic, topic_config_hash)
|
291
|
+
topic_ref = @topics_refs_map.dig(topic, topic_config_hash)
|
292
|
+
|
208
293
|
if partition_key
|
209
294
|
partition_count = partition_count(topic)
|
295
|
+
|
296
|
+
# Check if there are no overrides for the partitioner and use the default one only when
|
297
|
+
# no per-topic is present.
|
298
|
+
partitioner_name = @topics_configs.dig(topic, topic_config_hash, :partitioner) || @partitioner_name
|
299
|
+
|
210
300
|
# If the topic is not present, set to -1
|
211
301
|
partition = Rdkafka::Bindings.partitioner(partition_key, partition_count, @partitioner_name) if partition_count.positive?
|
212
302
|
end
|
@@ -229,6 +319,7 @@ module Rdkafka
|
|
229
319
|
|
230
320
|
delivery_handle = DeliveryHandle.new
|
231
321
|
delivery_handle.label = label
|
322
|
+
delivery_handle.topic = topic
|
232
323
|
delivery_handle[:pending] = true
|
233
324
|
delivery_handle[:response] = -1
|
234
325
|
delivery_handle[:partition] = -1
|
@@ -236,7 +327,7 @@ module Rdkafka
|
|
236
327
|
DeliveryHandle.register(delivery_handle)
|
237
328
|
|
238
329
|
args = [
|
239
|
-
:int, Rdkafka::Bindings::
|
330
|
+
:int, Rdkafka::Bindings::RD_KAFKA_VTYPE_RKT, :pointer, topic_ref,
|
240
331
|
:int, Rdkafka::Bindings::RD_KAFKA_VTYPE_MSGFLAGS, :int, Rdkafka::Bindings::RD_KAFKA_MSG_F_COPY,
|
241
332
|
:int, Rdkafka::Bindings::RD_KAFKA_VTYPE_VALUE, :buffer_in, payload, :size_t, payload_size,
|
242
333
|
:int, Rdkafka::Bindings::RD_KAFKA_VTYPE_KEY, :buffer_in, key, :size_t, key_size,
|
@@ -252,7 +343,7 @@ module Rdkafka
|
|
252
343
|
args << :int << Rdkafka::Bindings::RD_KAFKA_VTYPE_HEADER
|
253
344
|
args << :string << key
|
254
345
|
args << :pointer << value
|
255
|
-
args << :size_t << value.
|
346
|
+
args << :size_t << value.bytesize
|
256
347
|
end
|
257
348
|
end
|
258
349
|
|
data/lib/rdkafka/version.rb
CHANGED
data/lib/rdkafka.rb
CHANGED
@@ -24,7 +24,13 @@ require "rdkafka/admin/delete_acl_handle"
|
|
24
24
|
require "rdkafka/admin/delete_acl_report"
|
25
25
|
require "rdkafka/admin/describe_acl_handle"
|
26
26
|
require "rdkafka/admin/describe_acl_report"
|
27
|
+
require "rdkafka/admin/describe_configs_handle"
|
28
|
+
require "rdkafka/admin/describe_configs_report"
|
29
|
+
require "rdkafka/admin/incremental_alter_configs_handle"
|
30
|
+
require "rdkafka/admin/incremental_alter_configs_report"
|
27
31
|
require "rdkafka/admin/acl_binding_result"
|
32
|
+
require "rdkafka/admin/config_binding_result"
|
33
|
+
require "rdkafka/admin/config_resource_binding_result"
|
28
34
|
require "rdkafka/bindings"
|
29
35
|
require "rdkafka/callbacks"
|
30
36
|
require "rdkafka/config"
|
data/rdkafka.gemspec
CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.name = 'rdkafka'
|
16
16
|
gem.require_paths = ['lib']
|
17
17
|
gem.version = Rdkafka::VERSION
|
18
|
-
gem.required_ruby_version = '>=
|
18
|
+
gem.required_ruby_version = '>= 3.0'
|
19
19
|
gem.extensions = %w(ext/Rakefile)
|
20
20
|
gem.cert_chain = %w[certs/cert_chain.pem]
|
21
21
|
|