rdkafka 0.12.0 → 0.15.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
- checksums.yaml.gz.sig +0 -0
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/ci.yml +57 -0
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +155 -93
- data/Gemfile +2 -0
- data/{LICENSE → MIT-LICENSE} +2 -1
- data/README.md +76 -29
- data/Rakefile +2 -0
- data/certs/cert_chain.pem +26 -0
- data/docker-compose.yml +18 -15
- data/ext/README.md +1 -1
- data/ext/Rakefile +46 -27
- data/lib/rdkafka/abstract_handle.rb +41 -25
- data/lib/rdkafka/admin/acl_binding_result.rb +51 -0
- data/lib/rdkafka/admin/create_acl_handle.rb +28 -0
- data/lib/rdkafka/admin/create_acl_report.rb +24 -0
- data/lib/rdkafka/admin/create_partitions_handle.rb +27 -0
- data/lib/rdkafka/admin/create_partitions_report.rb +6 -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_acl_handle.rb +30 -0
- data/lib/rdkafka/admin/delete_acl_report.rb +23 -0
- data/lib/rdkafka/admin/delete_groups_handle.rb +28 -0
- data/lib/rdkafka/admin/delete_groups_report.rb +24 -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/describe_acl_handle.rb +30 -0
- data/lib/rdkafka/admin/describe_acl_report.rb +23 -0
- data/lib/rdkafka/admin.rb +494 -35
- data/lib/rdkafka/bindings.rb +180 -41
- data/lib/rdkafka/callbacks.rb +202 -1
- data/lib/rdkafka/config.rb +62 -25
- data/lib/rdkafka/consumer/headers.rb +24 -9
- data/lib/rdkafka/consumer/message.rb +3 -1
- data/lib/rdkafka/consumer/partition.rb +2 -0
- data/lib/rdkafka/consumer/topic_partition_list.rb +13 -8
- data/lib/rdkafka/consumer.rb +243 -111
- data/lib/rdkafka/error.rb +15 -0
- data/lib/rdkafka/helpers/time.rb +14 -0
- data/lib/rdkafka/metadata.rb +25 -2
- data/lib/rdkafka/native_kafka.rb +120 -0
- data/lib/rdkafka/producer/delivery_handle.rb +16 -2
- data/lib/rdkafka/producer/delivery_report.rb +22 -2
- data/lib/rdkafka/producer.rb +151 -21
- data/lib/rdkafka/version.rb +5 -3
- data/lib/rdkafka.rb +24 -2
- data/rdkafka.gemspec +21 -5
- data/renovate.json +6 -0
- data/spec/rdkafka/abstract_handle_spec.rb +1 -1
- data/spec/rdkafka/admin/create_acl_handle_spec.rb +56 -0
- data/spec/rdkafka/admin/create_acl_report_spec.rb +18 -0
- data/spec/rdkafka/admin/create_topic_handle_spec.rb +1 -1
- data/spec/rdkafka/admin/create_topic_report_spec.rb +1 -1
- data/spec/rdkafka/admin/delete_acl_handle_spec.rb +85 -0
- data/spec/rdkafka/admin/delete_acl_report_spec.rb +72 -0
- data/spec/rdkafka/admin/delete_topic_handle_spec.rb +1 -1
- data/spec/rdkafka/admin/delete_topic_report_spec.rb +1 -1
- data/spec/rdkafka/admin/describe_acl_handle_spec.rb +85 -0
- data/spec/rdkafka/admin/describe_acl_report_spec.rb +73 -0
- data/spec/rdkafka/admin_spec.rb +209 -5
- data/spec/rdkafka/bindings_spec.rb +2 -1
- data/spec/rdkafka/callbacks_spec.rb +1 -1
- data/spec/rdkafka/config_spec.rb +24 -3
- data/spec/rdkafka/consumer/headers_spec.rb +60 -0
- data/spec/rdkafka/consumer/message_spec.rb +1 -1
- data/spec/rdkafka/consumer/partition_spec.rb +1 -1
- data/spec/rdkafka/consumer/topic_partition_list_spec.rb +20 -1
- data/spec/rdkafka/consumer_spec.rb +352 -61
- data/spec/rdkafka/error_spec.rb +1 -1
- data/spec/rdkafka/metadata_spec.rb +4 -3
- data/spec/rdkafka/{producer/client_spec.rb → native_kafka_spec.rb} +13 -35
- data/spec/rdkafka/producer/delivery_handle_spec.rb +4 -1
- data/spec/rdkafka/producer/delivery_report_spec.rb +11 -3
- data/spec/rdkafka/producer_spec.rb +234 -22
- data/spec/spec_helper.rb +20 -2
- data.tar.gz.sig +0 -0
- metadata +81 -17
- metadata.gz.sig +0 -0
- data/.semaphore/semaphore.yml +0 -23
- data/bin/console +0 -11
- data/lib/rdkafka/producer/client.rb +0 -47
data/lib/rdkafka/config.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Rdkafka
|
4
4
|
# Configuration for a Kafka consumer or producer. You can create an instance and use
|
5
5
|
# the consumer and producer methods to create a client. Documentation of the available
|
6
|
-
# configuration options is available on https://github.com/
|
6
|
+
# configuration options is available on https://github.com/confluentinc/librdkafka/blob/master/CONFIGURATION.md.
|
7
7
|
class Config
|
8
8
|
# @private
|
9
9
|
@@logger = Logger.new(STDOUT)
|
@@ -12,7 +12,7 @@ module Rdkafka
|
|
12
12
|
# @private
|
13
13
|
@@error_callback = nil
|
14
14
|
# @private
|
15
|
-
@@opaques =
|
15
|
+
@@opaques = ObjectSpace::WeakMap.new
|
16
16
|
# @private
|
17
17
|
@@log_queue = Queue.new
|
18
18
|
|
@@ -30,7 +30,6 @@ module Rdkafka
|
|
30
30
|
@@logger
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
33
|
# Returns a queue whose contents will be passed to the configured logger. Each entry
|
35
34
|
# should follow the format [Logger::Severity, String]. The benefit over calling the
|
36
35
|
# logger directly is that this is safe to use from trap contexts.
|
@@ -47,18 +46,18 @@ module Rdkafka
|
|
47
46
|
# @return [nil]
|
48
47
|
def self.logger=(logger)
|
49
48
|
raise NoLoggerError if logger.nil?
|
50
|
-
@@logger=logger
|
49
|
+
@@logger = logger
|
51
50
|
end
|
52
51
|
|
53
52
|
# Set a callback that will be called every time the underlying client emits statistics.
|
54
53
|
# You can configure if and how often this happens using `statistics.interval.ms`.
|
55
|
-
# The callback is called with a hash that's documented here: https://github.com/
|
54
|
+
# The callback is called with a hash that's documented here: https://github.com/confluentinc/librdkafka/blob/master/STATISTICS.md
|
56
55
|
#
|
57
56
|
# @param callback [Proc, #call] The callback
|
58
57
|
#
|
59
58
|
# @return [nil]
|
60
59
|
def self.statistics_callback=(callback)
|
61
|
-
raise TypeError.new("Callback has to be callable") unless callback.respond_to?(:call)
|
60
|
+
raise TypeError.new("Callback has to be callable") unless callback.respond_to?(:call) || callback == nil
|
62
61
|
@@statistics_callback = callback
|
63
62
|
end
|
64
63
|
|
@@ -113,6 +112,7 @@ module Rdkafka
|
|
113
112
|
def initialize(config_hash = {})
|
114
113
|
@config_hash = DEFAULT_CONFIG.merge(config_hash)
|
115
114
|
@consumer_rebalance_listener = nil
|
115
|
+
@consumer_poll_set = true
|
116
116
|
end
|
117
117
|
|
118
118
|
# Set a config option.
|
@@ -141,12 +141,28 @@ module Rdkafka
|
|
141
141
|
@consumer_rebalance_listener = listener
|
142
142
|
end
|
143
143
|
|
144
|
-
#
|
144
|
+
# Should we use a single queue for the underlying consumer and events.
|
145
145
|
#
|
146
|
-
#
|
147
|
-
#
|
146
|
+
# This is an advanced API that allows for more granular control of the polling process.
|
147
|
+
# When this value is set to `false` (`true` by defualt), there will be two queues that need to
|
148
|
+
# be polled:
|
149
|
+
# - main librdkafka queue for events
|
150
|
+
# - consumer queue with messages and rebalances
|
151
|
+
#
|
152
|
+
# It is recommended to use the defaults and only set it to `false` in advance multi-threaded
|
153
|
+
# and complex cases where granular events handling control is needed.
|
154
|
+
#
|
155
|
+
# @param poll_set [Boolean]
|
156
|
+
def consumer_poll_set=(poll_set)
|
157
|
+
@consumer_poll_set = poll_set
|
158
|
+
end
|
159
|
+
|
160
|
+
# Creates a consumer with this configuration.
|
148
161
|
#
|
149
162
|
# @return [Consumer] The created consumer
|
163
|
+
#
|
164
|
+
# @raise [ConfigError] When the configuration contains invalid options
|
165
|
+
# @raise [ClientCreationError] When the native client cannot be created
|
150
166
|
def consumer
|
151
167
|
opaque = Opaque.new
|
152
168
|
config = native_config(opaque)
|
@@ -156,21 +172,28 @@ module Rdkafka
|
|
156
172
|
Rdkafka::Bindings.rd_kafka_conf_set_rebalance_cb(config, Rdkafka::Bindings::RebalanceCallback)
|
157
173
|
end
|
158
174
|
|
175
|
+
# Create native client
|
159
176
|
kafka = native_kafka(config, :rd_kafka_consumer)
|
160
177
|
|
161
|
-
# Redirect the main queue to the consumer
|
162
|
-
Rdkafka::Bindings.rd_kafka_poll_set_consumer(kafka)
|
178
|
+
# Redirect the main queue to the consumer queue
|
179
|
+
Rdkafka::Bindings.rd_kafka_poll_set_consumer(kafka) if @consumer_poll_set
|
163
180
|
|
164
181
|
# Return consumer with Kafka client
|
165
|
-
Rdkafka::Consumer.new(
|
182
|
+
Rdkafka::Consumer.new(
|
183
|
+
Rdkafka::NativeKafka.new(
|
184
|
+
kafka,
|
185
|
+
run_polling_thread: false,
|
186
|
+
opaque: opaque
|
187
|
+
)
|
188
|
+
)
|
166
189
|
end
|
167
190
|
|
168
191
|
# Create a producer with this configuration.
|
169
192
|
#
|
193
|
+
# @return [Producer] The created producer
|
194
|
+
#
|
170
195
|
# @raise [ConfigError] When the configuration contains invalid options
|
171
196
|
# @raise [ClientCreationError] When the native client cannot be created
|
172
|
-
#
|
173
|
-
# @return [Producer] The created producer
|
174
197
|
def producer
|
175
198
|
# Create opaque
|
176
199
|
opaque = Opaque.new
|
@@ -179,22 +202,36 @@ module Rdkafka
|
|
179
202
|
# Set callback to receive delivery reports on config
|
180
203
|
Rdkafka::Bindings.rd_kafka_conf_set_dr_msg_cb(config, Rdkafka::Callbacks::DeliveryCallbackFunction)
|
181
204
|
# Return producer with Kafka client
|
182
|
-
|
205
|
+
partitioner_name = self[:partitioner] || self["partitioner"]
|
206
|
+
Rdkafka::Producer.new(
|
207
|
+
Rdkafka::NativeKafka.new(
|
208
|
+
native_kafka(config, :rd_kafka_producer),
|
209
|
+
run_polling_thread: true,
|
210
|
+
opaque: opaque
|
211
|
+
),
|
212
|
+
partitioner_name
|
213
|
+
).tap do |producer|
|
183
214
|
opaque.producer = producer
|
184
215
|
end
|
185
216
|
end
|
186
217
|
|
187
|
-
#
|
218
|
+
# Creates an admin instance with this configuration.
|
219
|
+
#
|
220
|
+
# @return [Admin] The created admin instance
|
188
221
|
#
|
189
222
|
# @raise [ConfigError] When the configuration contains invalid options
|
190
223
|
# @raise [ClientCreationError] When the native client cannot be created
|
191
|
-
#
|
192
|
-
# @return [Admin] The created admin instance
|
193
224
|
def admin
|
194
225
|
opaque = Opaque.new
|
195
226
|
config = native_config(opaque)
|
196
227
|
Rdkafka::Bindings.rd_kafka_conf_set_background_event_cb(config, Rdkafka::Callbacks::BackgroundEventCallbackFunction)
|
197
|
-
Rdkafka::Admin.new(
|
228
|
+
Rdkafka::Admin.new(
|
229
|
+
Rdkafka::NativeKafka.new(
|
230
|
+
native_kafka(config, :rd_kafka_producer),
|
231
|
+
run_polling_thread: true,
|
232
|
+
opaque: opaque
|
233
|
+
)
|
234
|
+
)
|
198
235
|
end
|
199
236
|
|
200
237
|
# Error that is returned by the underlying rdkafka error if an invalid configuration option is present.
|
@@ -210,7 +247,7 @@ module Rdkafka
|
|
210
247
|
|
211
248
|
# This method is only intended to be used to create a client,
|
212
249
|
# using it in another way will leak memory.
|
213
|
-
def native_config(opaque=nil)
|
250
|
+
def native_config(opaque = nil)
|
214
251
|
Rdkafka::Bindings.rd_kafka_conf_new.tap do |config|
|
215
252
|
# Create config
|
216
253
|
@config_hash.merge(REQUIRED_CONFIG).each do |key, value|
|
@@ -282,18 +319,18 @@ module Rdkafka
|
|
282
319
|
producer.call_delivery_callback(delivery_report, delivery_handle) if producer
|
283
320
|
end
|
284
321
|
|
285
|
-
def call_on_partitions_assigned(
|
322
|
+
def call_on_partitions_assigned(list)
|
286
323
|
return unless consumer_rebalance_listener
|
287
324
|
return unless consumer_rebalance_listener.respond_to?(:on_partitions_assigned)
|
288
325
|
|
289
|
-
consumer_rebalance_listener.on_partitions_assigned(
|
326
|
+
consumer_rebalance_listener.on_partitions_assigned(list)
|
290
327
|
end
|
291
328
|
|
292
|
-
def call_on_partitions_revoked(
|
329
|
+
def call_on_partitions_revoked(list)
|
293
330
|
return unless consumer_rebalance_listener
|
294
331
|
return unless consumer_rebalance_listener.respond_to?(:on_partitions_revoked)
|
295
332
|
|
296
|
-
consumer_rebalance_listener.on_partitions_revoked(
|
333
|
+
consumer_rebalance_listener.on_partitions_revoked(list)
|
297
334
|
end
|
298
335
|
end
|
299
336
|
end
|
@@ -1,14 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rdkafka
|
2
4
|
class Consumer
|
3
|
-
#
|
4
|
-
|
5
|
-
|
5
|
+
# Interface to return headers for a consumer message
|
6
|
+
module Headers
|
7
|
+
class HashWithSymbolKeysTreatedLikeStrings < Hash
|
8
|
+
def [](key)
|
9
|
+
if key.is_a?(Symbol)
|
10
|
+
Kernel.warn("rdkafka deprecation warning: header access with Symbol key #{key.inspect} treated as a String. " \
|
11
|
+
"Please change your code to use String keys to avoid this warning. Symbol keys will break in version 1.")
|
12
|
+
super(key.to_s)
|
13
|
+
else
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Reads a librdkafka native message's headers and returns them as a Ruby Hash
|
6
20
|
#
|
7
|
-
# @
|
21
|
+
# @private
|
8
22
|
#
|
23
|
+
# @param [Rdkafka::Bindings::Message] native_message
|
24
|
+
# @return [Hash<String, String>] headers Hash for the native_message
|
9
25
|
# @raise [Rdkafka::RdkafkaError] when fail to read headers
|
10
|
-
#
|
11
|
-
# @private
|
12
26
|
def self.from_native(native_message)
|
13
27
|
headers_ptrptr = FFI::MemoryPointer.new(:pointer)
|
14
28
|
err = Rdkafka::Bindings.rd_kafka_message_headers(native_message, headers_ptrptr)
|
@@ -24,7 +38,8 @@ module Rdkafka
|
|
24
38
|
name_ptrptr = FFI::MemoryPointer.new(:pointer)
|
25
39
|
value_ptrptr = FFI::MemoryPointer.new(:pointer)
|
26
40
|
size_ptr = Rdkafka::Bindings::SizePtr.new
|
27
|
-
|
41
|
+
|
42
|
+
headers = HashWithSymbolKeysTreatedLikeStrings.new
|
28
43
|
|
29
44
|
idx = 0
|
30
45
|
loop do
|
@@ -51,12 +66,12 @@ module Rdkafka
|
|
51
66
|
|
52
67
|
value = value_ptr.read_string(size)
|
53
68
|
|
54
|
-
headers[name
|
69
|
+
headers[name] = value
|
55
70
|
|
56
71
|
idx += 1
|
57
72
|
end
|
58
73
|
|
59
|
-
headers
|
74
|
+
headers.freeze
|
60
75
|
end
|
61
76
|
end
|
62
77
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rdkafka
|
2
4
|
class Consumer
|
3
5
|
# A message that was consumed from a topic.
|
@@ -18,7 +20,7 @@ module Rdkafka
|
|
18
20
|
# @return [String, nil]
|
19
21
|
attr_reader :key
|
20
22
|
|
21
|
-
# This message's offset in
|
23
|
+
# This message's offset in its partition
|
22
24
|
# @return [Integer]
|
23
25
|
attr_reader :offset
|
24
26
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rdkafka
|
2
4
|
class Consumer
|
3
5
|
# A list of topics with their partition information
|
@@ -34,6 +36,11 @@ module Rdkafka
|
|
34
36
|
# Add a topic with optionally partitions to the list.
|
35
37
|
# Calling this method multiple times for the same topic will overwrite the previous configuraton.
|
36
38
|
#
|
39
|
+
# @param topic [String] The topic's name
|
40
|
+
# @param partitions [Array<Integer>, Range<Integer>, Integer] The topic's partitions or partition count
|
41
|
+
#
|
42
|
+
# @return [nil]
|
43
|
+
#
|
37
44
|
# @example Add a topic with unassigned partitions
|
38
45
|
# tpl.add_topic("topic")
|
39
46
|
#
|
@@ -43,10 +50,6 @@ module Rdkafka
|
|
43
50
|
# @example Add a topic with all topics up to a count
|
44
51
|
# tpl.add_topic("topic", 9)
|
45
52
|
#
|
46
|
-
# @param topic [String] The topic's name
|
47
|
-
# @param partitions [Array<Integer>, Range<Integer>, Integer] The topic's partitions or partition count
|
48
|
-
#
|
49
|
-
# @return [nil]
|
50
53
|
def add_topic(topic, partitions=nil)
|
51
54
|
if partitions.nil?
|
52
55
|
@data[topic.to_s] = nil
|
@@ -88,11 +91,11 @@ module Rdkafka
|
|
88
91
|
|
89
92
|
# Create a new topic partition list based of a native one.
|
90
93
|
#
|
94
|
+
# @private
|
95
|
+
#
|
91
96
|
# @param pointer [FFI::Pointer] Optional pointer to an existing native list. Its contents will be copied.
|
92
97
|
#
|
93
98
|
# @return [TopicPartitionList]
|
94
|
-
#
|
95
|
-
# @private
|
96
99
|
def self.from_native_tpl(pointer)
|
97
100
|
# Data to be moved into the tpl
|
98
101
|
data = {}
|
@@ -125,8 +128,8 @@ module Rdkafka
|
|
125
128
|
#
|
126
129
|
# The pointer will be cleaned by `rd_kafka_topic_partition_list_destroy` when GC releases it.
|
127
130
|
#
|
128
|
-
# @return [FFI::Pointer]
|
129
131
|
# @private
|
132
|
+
# @return [FFI::Pointer]
|
130
133
|
def to_native_tpl
|
131
134
|
tpl = Rdkafka::Bindings.rd_kafka_topic_partition_list_new(count)
|
132
135
|
|
@@ -140,11 +143,13 @@ module Rdkafka
|
|
140
143
|
)
|
141
144
|
|
142
145
|
if p.offset
|
146
|
+
offset = p.offset.is_a?(Time) ? p.offset.to_f * 1_000 : p.offset
|
147
|
+
|
143
148
|
Rdkafka::Bindings.rd_kafka_topic_partition_list_set_offset(
|
144
149
|
tpl,
|
145
150
|
topic,
|
146
151
|
p.partition,
|
147
|
-
|
152
|
+
offset
|
148
153
|
)
|
149
154
|
end
|
150
155
|
end
|