rdkafka 0.13.1 → 0.14.0.rc1
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 +1 -0
- data/.github/workflows/ci.yml +58 -0
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +39 -24
- data/{LICENSE → MIT-LICENSE} +2 -1
- data/README.md +19 -20
- data/certs/cert_chain.pem +26 -0
- data/docker-compose.yml +16 -15
- data/ext/README.md +1 -1
- data/ext/Rakefile +26 -53
- data/lib/rdkafka/abstract_handle.rb +37 -24
- data/lib/rdkafka/admin.rb +6 -7
- data/lib/rdkafka/bindings.rb +8 -5
- data/lib/rdkafka/config.rb +30 -17
- data/lib/rdkafka/consumer/headers.rb +2 -4
- data/lib/rdkafka/consumer/topic_partition_list.rb +3 -1
- data/lib/rdkafka/consumer.rb +92 -53
- data/lib/rdkafka/helpers/time.rb +14 -0
- data/lib/rdkafka/metadata.rb +22 -1
- data/lib/rdkafka/native_kafka.rb +6 -1
- data/lib/rdkafka/producer.rb +85 -7
- data/lib/rdkafka/version.rb +3 -3
- data/lib/rdkafka.rb +10 -1
- data/rdkafka.gemspec +17 -3
- data/renovate.json +6 -0
- data/spec/rdkafka/abstract_handle_spec.rb +0 -2
- data/spec/rdkafka/admin/create_topic_handle_spec.rb +0 -2
- data/spec/rdkafka/admin/create_topic_report_spec.rb +0 -2
- data/spec/rdkafka/admin/delete_topic_handle_spec.rb +0 -2
- data/spec/rdkafka/admin/delete_topic_report_spec.rb +0 -2
- data/spec/rdkafka/admin_spec.rb +1 -2
- data/spec/rdkafka/bindings_spec.rb +0 -1
- data/spec/rdkafka/callbacks_spec.rb +0 -2
- data/spec/rdkafka/config_spec.rb +0 -2
- data/spec/rdkafka/consumer/headers_spec.rb +0 -2
- data/spec/rdkafka/consumer/message_spec.rb +0 -2
- data/spec/rdkafka/consumer/partition_spec.rb +0 -2
- data/spec/rdkafka/consumer/topic_partition_list_spec.rb +19 -2
- data/spec/rdkafka/consumer_spec.rb +143 -39
- data/spec/rdkafka/error_spec.rb +0 -2
- data/spec/rdkafka/metadata_spec.rb +2 -3
- data/spec/rdkafka/native_kafka_spec.rb +2 -3
- data/spec/rdkafka/producer/delivery_handle_spec.rb +0 -2
- data/spec/rdkafka/producer/delivery_report_spec.rb +0 -2
- data/spec/rdkafka/producer_spec.rb +157 -1
- data.tar.gz.sig +0 -0
- metadata +51 -13
- metadata.gz.sig +0 -0
- data/.semaphore/semaphore.yml +0 -27
- data/dist/librdkafka_2.0.2.tar.gz +0 -0
data/lib/rdkafka/bindings.rb
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "ffi"
|
4
|
-
require "json"
|
5
|
-
require "logger"
|
6
|
-
|
7
3
|
module Rdkafka
|
8
4
|
# @private
|
9
5
|
module Bindings
|
@@ -35,12 +31,13 @@ module Rdkafka
|
|
35
31
|
|
36
32
|
# Polling
|
37
33
|
|
38
|
-
attach_function :rd_kafka_flush, [:pointer, :int], :
|
34
|
+
attach_function :rd_kafka_flush, [:pointer, :int], :int, blocking: true
|
39
35
|
attach_function :rd_kafka_poll, [:pointer, :int], :void, blocking: true
|
40
36
|
attach_function :rd_kafka_outq_len, [:pointer], :int, blocking: true
|
41
37
|
|
42
38
|
# Metadata
|
43
39
|
|
40
|
+
attach_function :rd_kafka_name, [:pointer], :string, blocking: true
|
44
41
|
attach_function :rd_kafka_memberid, [:pointer], :string, blocking: true
|
45
42
|
attach_function :rd_kafka_clusterid, [:pointer], :string, blocking: true
|
46
43
|
attach_function :rd_kafka_metadata, [:pointer, :int, :pointer, :pointer, :int], :int, blocking: true
|
@@ -157,6 +154,7 @@ module Rdkafka
|
|
157
154
|
) do |_client_prr, err_code, reason, _opaque|
|
158
155
|
if Rdkafka::Config.error_callback
|
159
156
|
error = Rdkafka::RdkafkaError.new(err_code, broker_message: reason)
|
157
|
+
error.set_backtrace(caller)
|
160
158
|
Rdkafka::Config.error_callback.call(error)
|
161
159
|
end
|
162
160
|
end
|
@@ -190,6 +188,8 @@ module Rdkafka
|
|
190
188
|
attach_function :rd_kafka_pause_partitions, [:pointer, :pointer], :int, blocking: true
|
191
189
|
attach_function :rd_kafka_resume_partitions, [:pointer, :pointer], :int, blocking: true
|
192
190
|
attach_function :rd_kafka_seek, [:pointer, :int32, :int64, :int], :int, blocking: true
|
191
|
+
attach_function :rd_kafka_offsets_for_times, [:pointer, :pointer, :int], :int, blocking: true
|
192
|
+
attach_function :rd_kafka_position, [:pointer, :pointer], :int, blocking: true
|
193
193
|
|
194
194
|
# Headers
|
195
195
|
attach_function :rd_kafka_header_get_all, [:pointer, :size_t, :pointer, :pointer, SizePtr], :int
|
@@ -251,10 +251,13 @@ module Rdkafka
|
|
251
251
|
RD_KAFKA_VTYPE_TIMESTAMP = 8
|
252
252
|
RD_KAFKA_VTYPE_HEADER = 9
|
253
253
|
RD_KAFKA_VTYPE_HEADERS = 10
|
254
|
+
RD_KAFKA_PURGE_F_QUEUE = 1
|
255
|
+
RD_KAFKA_PURGE_F_INFLIGHT = 2
|
254
256
|
|
255
257
|
RD_KAFKA_MSG_F_COPY = 0x2
|
256
258
|
|
257
259
|
attach_function :rd_kafka_producev, [:pointer, :varargs], :int, blocking: true
|
260
|
+
attach_function :rd_kafka_purge, [:pointer, :int], :int, blocking: true
|
258
261
|
callback :delivery_cb, [:pointer, :pointer, :pointer], :void
|
259
262
|
attach_function :rd_kafka_conf_set_dr_msg_cb, [:pointer, :delivery_cb], :void
|
260
263
|
|
data/lib/rdkafka/config.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "logger"
|
4
|
-
|
5
3
|
module Rdkafka
|
6
4
|
# Configuration for a Kafka consumer or producer. You can create an instance and use
|
7
5
|
# the consumer and producer methods to create a client. Documentation of the available
|
8
|
-
# configuration options is available on https://github.com/
|
6
|
+
# configuration options is available on https://github.com/confluentinc/librdkafka/blob/master/CONFIGURATION.md.
|
9
7
|
class Config
|
10
8
|
# @private
|
11
9
|
@@logger = Logger.new(STDOUT)
|
@@ -14,7 +12,7 @@ module Rdkafka
|
|
14
12
|
# @private
|
15
13
|
@@error_callback = nil
|
16
14
|
# @private
|
17
|
-
@@opaques =
|
15
|
+
@@opaques = ObjectSpace::WeakMap.new
|
18
16
|
# @private
|
19
17
|
@@log_queue = Queue.new
|
20
18
|
|
@@ -53,13 +51,13 @@ module Rdkafka
|
|
53
51
|
|
54
52
|
# Set a callback that will be called every time the underlying client emits statistics.
|
55
53
|
# You can configure if and how often this happens using `statistics.interval.ms`.
|
56
|
-
# 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
|
57
55
|
#
|
58
56
|
# @param callback [Proc, #call] The callback
|
59
57
|
#
|
60
58
|
# @return [nil]
|
61
59
|
def self.statistics_callback=(callback)
|
62
|
-
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
|
63
61
|
@@statistics_callback = callback
|
64
62
|
end
|
65
63
|
|
@@ -142,12 +140,12 @@ module Rdkafka
|
|
142
140
|
@consumer_rebalance_listener = listener
|
143
141
|
end
|
144
142
|
|
145
|
-
#
|
143
|
+
# Creates a consumer with this configuration.
|
144
|
+
#
|
145
|
+
# @return [Consumer] The created consumer
|
146
146
|
#
|
147
147
|
# @raise [ConfigError] When the configuration contains invalid options
|
148
148
|
# @raise [ClientCreationError] When the native client cannot be created
|
149
|
-
#
|
150
|
-
# @return [Consumer] The created consumer
|
151
149
|
def consumer
|
152
150
|
opaque = Opaque.new
|
153
151
|
config = native_config(opaque)
|
@@ -164,15 +162,21 @@ module Rdkafka
|
|
164
162
|
Rdkafka::Bindings.rd_kafka_poll_set_consumer(kafka)
|
165
163
|
|
166
164
|
# Return consumer with Kafka client
|
167
|
-
Rdkafka::Consumer.new(
|
165
|
+
Rdkafka::Consumer.new(
|
166
|
+
Rdkafka::NativeKafka.new(
|
167
|
+
kafka,
|
168
|
+
run_polling_thread: false,
|
169
|
+
opaque: opaque
|
170
|
+
)
|
171
|
+
)
|
168
172
|
end
|
169
173
|
|
170
174
|
# Create a producer with this configuration.
|
171
175
|
#
|
176
|
+
# @return [Producer] The created producer
|
177
|
+
#
|
172
178
|
# @raise [ConfigError] When the configuration contains invalid options
|
173
179
|
# @raise [ClientCreationError] When the native client cannot be created
|
174
|
-
#
|
175
|
-
# @return [Producer] The created producer
|
176
180
|
def producer
|
177
181
|
# Create opaque
|
178
182
|
opaque = Opaque.new
|
@@ -182,22 +186,31 @@ module Rdkafka
|
|
182
186
|
Rdkafka::Bindings.rd_kafka_conf_set_dr_msg_cb(config, Rdkafka::Callbacks::DeliveryCallbackFunction)
|
183
187
|
# Return producer with Kafka client
|
184
188
|
partitioner_name = self[:partitioner] || self["partitioner"]
|
185
|
-
Rdkafka::Producer.new(
|
189
|
+
Rdkafka::Producer.new(
|
190
|
+
Rdkafka::NativeKafka.new(native_kafka(config, :rd_kafka_producer), run_polling_thread: true, opaque: opaque),
|
191
|
+
partitioner_name
|
192
|
+
).tap do |producer|
|
186
193
|
opaque.producer = producer
|
187
194
|
end
|
188
195
|
end
|
189
196
|
|
190
|
-
#
|
197
|
+
# Creates an admin instance with this configuration.
|
198
|
+
#
|
199
|
+
# @return [Admin] The created admin instance
|
191
200
|
#
|
192
201
|
# @raise [ConfigError] When the configuration contains invalid options
|
193
202
|
# @raise [ClientCreationError] When the native client cannot be created
|
194
|
-
#
|
195
|
-
# @return [Admin] The created admin instance
|
196
203
|
def admin
|
197
204
|
opaque = Opaque.new
|
198
205
|
config = native_config(opaque)
|
199
206
|
Rdkafka::Bindings.rd_kafka_conf_set_background_event_cb(config, Rdkafka::Callbacks::BackgroundEventCallbackFunction)
|
200
|
-
Rdkafka::Admin.new(
|
207
|
+
Rdkafka::Admin.new(
|
208
|
+
Rdkafka::NativeKafka.new(
|
209
|
+
native_kafka(config, :rd_kafka_producer),
|
210
|
+
run_polling_thread: true,
|
211
|
+
opaque: opaque
|
212
|
+
)
|
213
|
+
)
|
201
214
|
end
|
202
215
|
|
203
216
|
# Error that is returned by the underlying rdkafka error if an invalid configuration option is present.
|
@@ -18,13 +18,11 @@ module Rdkafka
|
|
18
18
|
|
19
19
|
# Reads a librdkafka native message's headers and returns them as a Ruby Hash
|
20
20
|
#
|
21
|
-
# @
|
21
|
+
# @private
|
22
22
|
#
|
23
|
+
# @param [librdkakfa message] native_message
|
23
24
|
# @return [Hash<String, String>] headers Hash for the native_message
|
24
|
-
#
|
25
25
|
# @raise [Rdkafka::RdkafkaError] when fail to read headers
|
26
|
-
#
|
27
|
-
# @private
|
28
26
|
def self.from_native(native_message)
|
29
27
|
headers_ptrptr = FFI::MemoryPointer.new(:pointer)
|
30
28
|
err = Rdkafka::Bindings.rd_kafka_message_headers(native_message, headers_ptrptr)
|
@@ -142,11 +142,13 @@ module Rdkafka
|
|
142
142
|
)
|
143
143
|
|
144
144
|
if p.offset
|
145
|
+
offset = p.offset.is_a?(Time) ? p.offset.to_f * 1_000 : p.offset
|
146
|
+
|
145
147
|
Rdkafka::Bindings.rd_kafka_topic_partition_list_set_offset(
|
146
148
|
tpl,
|
147
149
|
topic,
|
148
150
|
p.partition,
|
149
|
-
|
151
|
+
offset
|
150
152
|
)
|
151
153
|
end
|
152
154
|
end
|
data/lib/rdkafka/consumer.rb
CHANGED
@@ -12,6 +12,7 @@ module Rdkafka
|
|
12
12
|
# `each_slice` to consume batches of messages.
|
13
13
|
class Consumer
|
14
14
|
include Enumerable
|
15
|
+
include Helpers::Time
|
15
16
|
|
16
17
|
# @private
|
17
18
|
def initialize(native_kafka)
|
@@ -22,6 +23,13 @@ module Rdkafka
|
|
22
23
|
->(_) { close }
|
23
24
|
end
|
24
25
|
|
26
|
+
# @return [String] consumer name
|
27
|
+
def name
|
28
|
+
@name ||= @native_kafka.with_inner do |inner|
|
29
|
+
::Rdkafka::Bindings.rd_kafka_name(inner)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
25
33
|
# Close this consumer
|
26
34
|
# @return [nil]
|
27
35
|
def close
|
@@ -40,13 +48,11 @@ module Rdkafka
|
|
40
48
|
@native_kafka.closed?
|
41
49
|
end
|
42
50
|
|
43
|
-
#
|
51
|
+
# Subscribes to one or more topics letting Kafka handle partition assignments.
|
44
52
|
#
|
45
53
|
# @param topics [Array<String>] One or more topic names
|
46
|
-
#
|
47
|
-
# @raise [RdkafkaError] When subscribing fails
|
48
|
-
#
|
49
54
|
# @return [nil]
|
55
|
+
# @raise [RdkafkaError] When subscribing fails
|
50
56
|
def subscribe(*topics)
|
51
57
|
closed_consumer_check(__method__)
|
52
58
|
|
@@ -70,9 +76,8 @@ module Rdkafka
|
|
70
76
|
|
71
77
|
# Unsubscribe from all subscribed topics.
|
72
78
|
#
|
73
|
-
# @raise [RdkafkaError] When unsubscribing fails
|
74
|
-
#
|
75
79
|
# @return [nil]
|
80
|
+
# @raise [RdkafkaError] When unsubscribing fails
|
76
81
|
def unsubscribe
|
77
82
|
closed_consumer_check(__method__)
|
78
83
|
|
@@ -87,10 +92,8 @@ module Rdkafka
|
|
87
92
|
# Pause producing or consumption for the provided list of partitions
|
88
93
|
#
|
89
94
|
# @param list [TopicPartitionList] The topic with partitions to pause
|
90
|
-
#
|
91
|
-
# @raise [RdkafkaTopicPartitionListError] When pausing subscription fails.
|
92
|
-
#
|
93
95
|
# @return [nil]
|
96
|
+
# @raise [RdkafkaTopicPartitionListError] When pausing subscription fails.
|
94
97
|
def pause(list)
|
95
98
|
closed_consumer_check(__method__)
|
96
99
|
|
@@ -114,13 +117,11 @@ module Rdkafka
|
|
114
117
|
end
|
115
118
|
end
|
116
119
|
|
117
|
-
#
|
120
|
+
# Resumes producing consumption for the provided list of partitions
|
118
121
|
#
|
119
122
|
# @param list [TopicPartitionList] The topic with partitions to pause
|
120
|
-
#
|
121
|
-
# @raise [RdkafkaError] When resume subscription fails.
|
122
|
-
#
|
123
123
|
# @return [nil]
|
124
|
+
# @raise [RdkafkaError] When resume subscription fails.
|
124
125
|
def resume(list)
|
125
126
|
closed_consumer_check(__method__)
|
126
127
|
|
@@ -142,11 +143,10 @@ module Rdkafka
|
|
142
143
|
end
|
143
144
|
end
|
144
145
|
|
145
|
-
#
|
146
|
-
#
|
147
|
-
# @raise [RdkafkaError] When getting the subscription fails.
|
146
|
+
# Returns the current subscription to topics and partitions
|
148
147
|
#
|
149
148
|
# @return [TopicPartitionList]
|
149
|
+
# @raise [RdkafkaError] When getting the subscription fails.
|
150
150
|
def subscription
|
151
151
|
closed_consumer_check(__method__)
|
152
152
|
|
@@ -171,7 +171,6 @@ module Rdkafka
|
|
171
171
|
# Atomic assignment of partitions to consume
|
172
172
|
#
|
173
173
|
# @param list [TopicPartitionList] The topic with partitions to assign
|
174
|
-
#
|
175
174
|
# @raise [RdkafkaError] When assigning fails
|
176
175
|
def assign(list)
|
177
176
|
closed_consumer_check(__method__)
|
@@ -196,9 +195,8 @@ module Rdkafka
|
|
196
195
|
|
197
196
|
# Returns the current partition assignment.
|
198
197
|
#
|
199
|
-
# @raise [RdkafkaError] When getting the assignment fails.
|
200
|
-
#
|
201
198
|
# @return [TopicPartitionList]
|
199
|
+
# @raise [RdkafkaError] When getting the assignment fails.
|
202
200
|
def assignment
|
203
201
|
closed_consumer_check(__method__)
|
204
202
|
|
@@ -224,14 +222,14 @@ module Rdkafka
|
|
224
222
|
end
|
225
223
|
|
226
224
|
# Return the current committed offset per partition for this consumer group.
|
227
|
-
# The offset field of each requested partition will either be set to stored offset or to -1001
|
225
|
+
# The offset field of each requested partition will either be set to stored offset or to -1001
|
226
|
+
# in case there was no stored offset for that partition.
|
228
227
|
#
|
229
|
-
# @param list [TopicPartitionList, nil] The topic with partitions to get the offsets for or nil
|
228
|
+
# @param list [TopicPartitionList, nil] The topic with partitions to get the offsets for or nil
|
229
|
+
# to use the current subscription.
|
230
230
|
# @param timeout_ms [Integer] The timeout for fetching this information.
|
231
|
-
#
|
232
|
-
# @raise [RdkafkaError] When getting the committed positions fails.
|
233
|
-
#
|
234
231
|
# @return [TopicPartitionList]
|
232
|
+
# @raise [RdkafkaError] When getting the committed positions fails.
|
235
233
|
def committed(list=nil, timeout_ms=1200)
|
236
234
|
closed_consumer_check(__method__)
|
237
235
|
|
@@ -256,15 +254,41 @@ module Rdkafka
|
|
256
254
|
end
|
257
255
|
end
|
258
256
|
|
257
|
+
# Return the current positions (offsets) for topics and partitions.
|
258
|
+
# The offset field of each requested partition will be set to the offset of the last consumed message + 1, or nil in case there was no previous message.
|
259
|
+
#
|
260
|
+
# @param list [TopicPartitionList, nil] The topic with partitions to get the offsets for or nil to use the current subscription.
|
261
|
+
#
|
262
|
+
# @raise [RdkafkaError] When getting the positions fails.
|
263
|
+
#
|
264
|
+
# @return [TopicPartitionList]
|
265
|
+
def position(list=nil)
|
266
|
+
if list.nil?
|
267
|
+
list = assignment
|
268
|
+
elsif !list.is_a?(TopicPartitionList)
|
269
|
+
raise TypeError.new("list has to be nil or a TopicPartitionList")
|
270
|
+
end
|
271
|
+
|
272
|
+
tpl = list.to_native_tpl
|
273
|
+
|
274
|
+
response = @native_kafka.with_inner do |inner|
|
275
|
+
Rdkafka::Bindings.rd_kafka_position(inner, tpl)
|
276
|
+
end
|
277
|
+
|
278
|
+
if response != 0
|
279
|
+
raise Rdkafka::RdkafkaError.new(response)
|
280
|
+
end
|
281
|
+
|
282
|
+
TopicPartitionList.from_native_tpl(tpl)
|
283
|
+
end
|
284
|
+
|
259
285
|
# Query broker for low (oldest/beginning) and high (newest/end) offsets for a partition.
|
260
286
|
#
|
261
287
|
# @param topic [String] The topic to query
|
262
288
|
# @param partition [Integer] The partition to query
|
263
289
|
# @param timeout_ms [Integer] The timeout for querying the broker
|
264
|
-
#
|
265
|
-
# @raise [RdkafkaError] When querying the broker fails.
|
266
|
-
#
|
267
290
|
# @return [Integer] The low and high watermark
|
291
|
+
# @raise [RdkafkaError] When querying the broker fails.
|
268
292
|
def query_watermark_offsets(topic, partition, timeout_ms=200)
|
269
293
|
closed_consumer_check(__method__)
|
270
294
|
|
@@ -298,10 +322,9 @@ module Rdkafka
|
|
298
322
|
#
|
299
323
|
# @param topic_partition_list [TopicPartitionList] The list to calculate lag for.
|
300
324
|
# @param watermark_timeout_ms [Integer] The timeout for each query watermark call.
|
301
|
-
#
|
325
|
+
# @return [Hash<String, Hash<Integer, Integer>>] A hash containing all topics with the lag
|
326
|
+
# per partition
|
302
327
|
# @raise [RdkafkaError] When querying the broker fails.
|
303
|
-
#
|
304
|
-
# @return [Hash<String, Hash<Integer, Integer>>] A hash containing all topics with the lag per partition
|
305
328
|
def lag(topic_partition_list, watermark_timeout_ms=100)
|
306
329
|
out = {}
|
307
330
|
|
@@ -350,10 +373,8 @@ module Rdkafka
|
|
350
373
|
# When using this `enable.auto.offset.store` should be set to `false` in the config.
|
351
374
|
#
|
352
375
|
# @param message [Rdkafka::Consumer::Message] The message which offset will be stored
|
353
|
-
#
|
354
|
-
# @raise [RdkafkaError] When storing the offset fails
|
355
|
-
#
|
356
376
|
# @return [nil]
|
377
|
+
# @raise [RdkafkaError] When storing the offset fails
|
357
378
|
def store_offset(message)
|
358
379
|
closed_consumer_check(__method__)
|
359
380
|
|
@@ -384,10 +405,8 @@ module Rdkafka
|
|
384
405
|
# message at the given offset.
|
385
406
|
#
|
386
407
|
# @param message [Rdkafka::Consumer::Message] The message to which to seek
|
387
|
-
#
|
388
|
-
# @raise [RdkafkaError] When seeking fails
|
389
|
-
#
|
390
408
|
# @return [nil]
|
409
|
+
# @raise [RdkafkaError] When seeking fails
|
391
410
|
def seek(message)
|
392
411
|
closed_consumer_check(__method__)
|
393
412
|
|
@@ -415,6 +434,39 @@ module Rdkafka
|
|
415
434
|
end
|
416
435
|
end
|
417
436
|
|
437
|
+
# Lookup offset for the given partitions by timestamp.
|
438
|
+
#
|
439
|
+
# @param list [TopicPartitionList] The TopicPartitionList with timestamps instead of offsets
|
440
|
+
#
|
441
|
+
# @raise [RdKafkaError] When the OffsetForTimes lookup fails
|
442
|
+
#
|
443
|
+
# @return [TopicPartitionList]
|
444
|
+
def offsets_for_times(list, timeout_ms = 1000)
|
445
|
+
closed_consumer_check(__method__)
|
446
|
+
|
447
|
+
if !list.is_a?(TopicPartitionList)
|
448
|
+
raise TypeError.new("list has to be a TopicPartitionList")
|
449
|
+
end
|
450
|
+
|
451
|
+
tpl = list.to_native_tpl
|
452
|
+
|
453
|
+
response = @native_kafka.with_inner do |inner|
|
454
|
+
Rdkafka::Bindings.rd_kafka_offsets_for_times(
|
455
|
+
inner,
|
456
|
+
tpl,
|
457
|
+
timeout_ms # timeout
|
458
|
+
)
|
459
|
+
end
|
460
|
+
|
461
|
+
if response != 0
|
462
|
+
raise Rdkafka::RdkafkaError.new(response)
|
463
|
+
end
|
464
|
+
|
465
|
+
TopicPartitionList.from_native_tpl(tpl)
|
466
|
+
ensure
|
467
|
+
Rdkafka::Bindings.rd_kafka_topic_partition_list_destroy(tpl) if tpl
|
468
|
+
end
|
469
|
+
|
418
470
|
# Manually commit the current offsets of this consumer.
|
419
471
|
#
|
420
472
|
# To use this set `enable.auto.commit`to `false` to disable automatic triggering
|
@@ -426,10 +478,8 @@ module Rdkafka
|
|
426
478
|
#
|
427
479
|
# @param list [TopicPartitionList,nil] The topic with partitions to commit
|
428
480
|
# @param async [Boolean] Whether to commit async or wait for the commit to finish
|
429
|
-
#
|
430
|
-
# @raise [RdkafkaError] When committing fails
|
431
|
-
#
|
432
481
|
# @return [nil]
|
482
|
+
# @raise [RdkafkaError] When committing fails
|
433
483
|
def commit(list=nil, async=false)
|
434
484
|
closed_consumer_check(__method__)
|
435
485
|
|
@@ -454,10 +504,8 @@ module Rdkafka
|
|
454
504
|
# Poll for the next message on one of the subscribed topics
|
455
505
|
#
|
456
506
|
# @param timeout_ms [Integer] Timeout of this poll
|
457
|
-
#
|
458
|
-
# @raise [RdkafkaError] When polling fails
|
459
|
-
#
|
460
507
|
# @return [Message, nil] A message or nil if there was no new message within the timeout
|
508
|
+
# @raise [RdkafkaError] When polling fails
|
461
509
|
def poll(timeout_ms)
|
462
510
|
closed_consumer_check(__method__)
|
463
511
|
|
@@ -486,14 +534,11 @@ module Rdkafka
|
|
486
534
|
# Poll for new messages and yield for each received one. Iteration
|
487
535
|
# will end when the consumer is closed.
|
488
536
|
#
|
489
|
-
# If `enable.partition.eof` is turned on in the config this will raise an
|
490
|
-
#
|
491
|
-
# using this method of iteration.
|
537
|
+
# If `enable.partition.eof` is turned on in the config this will raise an error when an eof is
|
538
|
+
# reached, so you probably want to disable that when using this method of iteration.
|
492
539
|
#
|
493
540
|
# @raise [RdkafkaError] When polling fails
|
494
|
-
#
|
495
541
|
# @yieldparam message [Message] Received message
|
496
|
-
#
|
497
542
|
# @return [nil]
|
498
543
|
def each
|
499
544
|
loop do
|
@@ -546,9 +591,7 @@ module Rdkafka
|
|
546
591
|
# that you may or may not see again.
|
547
592
|
#
|
548
593
|
# @param max_items [Integer] Maximum size of the yielded array of messages
|
549
|
-
#
|
550
594
|
# @param bytes_threshold [Integer] Threshold number of total message bytes in the yielded array of messages
|
551
|
-
#
|
552
595
|
# @param timeout_ms [Integer] max time to wait for up to max_items
|
553
596
|
#
|
554
597
|
# @raise [RdkafkaError] When polling fails
|
@@ -595,10 +638,6 @@ module Rdkafka
|
|
595
638
|
end
|
596
639
|
|
597
640
|
private
|
598
|
-
def monotonic_now
|
599
|
-
# needed because Time.now can go backwards
|
600
|
-
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
601
|
-
end
|
602
641
|
|
603
642
|
def closed_consumer_check(method)
|
604
643
|
raise Rdkafka::ClosedConsumerError.new(method) if closed?
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rdkafka
|
4
|
+
# Namespace for some small utilities used in multiple components
|
5
|
+
module Helpers
|
6
|
+
# Time related methods used across Karafka
|
7
|
+
module Time
|
8
|
+
# @return [Float] current monotonic time in seconds with microsecond precision
|
9
|
+
def monotonic_now
|
10
|
+
::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/rdkafka/metadata.rb
CHANGED
@@ -4,7 +4,18 @@ module Rdkafka
|
|
4
4
|
class Metadata
|
5
5
|
attr_reader :brokers, :topics
|
6
6
|
|
7
|
-
|
7
|
+
# Errors upon which we retry the metadata fetch
|
8
|
+
RETRIED_ERRORS = %i[
|
9
|
+
timed_out
|
10
|
+
leader_not_available
|
11
|
+
].freeze
|
12
|
+
|
13
|
+
private_constant :RETRIED_ERRORS
|
14
|
+
|
15
|
+
def initialize(native_client, topic_name = nil, timeout_ms = 2_000)
|
16
|
+
attempt ||= 0
|
17
|
+
attempt += 1
|
18
|
+
|
8
19
|
native_topic = if topic_name
|
9
20
|
Rdkafka::Bindings.rd_kafka_topic_new(native_client, topic_name, nil)
|
10
21
|
end
|
@@ -22,6 +33,16 @@ module Rdkafka
|
|
22
33
|
raise Rdkafka::RdkafkaError.new(result) unless result.zero?
|
23
34
|
|
24
35
|
metadata_from_native(ptr.read_pointer)
|
36
|
+
rescue ::Rdkafka::RdkafkaError => e
|
37
|
+
raise unless RETRIED_ERRORS.include?(e.code)
|
38
|
+
raise if attempt > 10
|
39
|
+
|
40
|
+
backoff_factor = 2**attempt
|
41
|
+
timeout = backoff_factor * 0.1
|
42
|
+
|
43
|
+
sleep(timeout)
|
44
|
+
|
45
|
+
retry
|
25
46
|
ensure
|
26
47
|
Rdkafka::Bindings.rd_kafka_topic_destroy(native_topic) if topic_name
|
27
48
|
Rdkafka::Bindings.rd_kafka_metadata_destroy(ptr.read_pointer)
|
data/lib/rdkafka/native_kafka.rb
CHANGED
@@ -4,8 +4,9 @@ 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, run_polling_thread:)
|
7
|
+
def initialize(inner, run_polling_thread:, opaque:)
|
8
8
|
@inner = inner
|
9
|
+
@opaque = opaque
|
9
10
|
# Lock around external access
|
10
11
|
@access_mutex = Mutex.new
|
11
12
|
# Lock around internal polling
|
@@ -27,6 +28,9 @@ module Rdkafka
|
|
27
28
|
# counter for operations in progress using inner
|
28
29
|
@operations_in_progress = 0
|
29
30
|
|
31
|
+
# Trigger initial poll to make sure oauthbearer cb and other initial cb are handled
|
32
|
+
Rdkafka::Bindings.rd_kafka_poll(inner, 0)
|
33
|
+
|
30
34
|
if run_polling_thread
|
31
35
|
# Start thread to poll client for delivery callbacks,
|
32
36
|
# not used in consumer.
|
@@ -109,6 +113,7 @@ module Rdkafka
|
|
109
113
|
|
110
114
|
Rdkafka::Bindings.rd_kafka_destroy(@inner)
|
111
115
|
@inner = nil
|
116
|
+
@opaque = nil
|
112
117
|
end
|
113
118
|
end
|
114
119
|
end
|