rdkafka 0.24.2-aarch64-linux-gnu → 0.25.1-aarch64-linux-gnu
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/CHANGELOG.md +18 -0
- data/Gemfile +8 -0
- data/Gemfile.lint +14 -0
- data/Gemfile.lint.lock +123 -0
- data/README.md +2 -1
- data/Rakefile +21 -21
- data/docker-compose-ssl.yml +1 -1
- data/docker-compose.yml +1 -1
- data/ext/librdkafka.so +0 -0
- data/lib/rdkafka/abstract_handle.rb +23 -5
- data/lib/rdkafka/admin/acl_binding_result.rb +5 -5
- data/lib/rdkafka/admin/config_resource_binding_result.rb +1 -0
- data/lib/rdkafka/admin/create_acl_handle.rb +7 -4
- data/lib/rdkafka/admin/create_acl_report.rb +3 -2
- data/lib/rdkafka/admin/create_partitions_handle.rb +8 -5
- data/lib/rdkafka/admin/create_partitions_report.rb +1 -0
- data/lib/rdkafka/admin/create_topic_handle.rb +8 -5
- data/lib/rdkafka/admin/create_topic_report.rb +3 -0
- data/lib/rdkafka/admin/delete_acl_handle.rb +9 -6
- data/lib/rdkafka/admin/delete_acl_report.rb +5 -3
- data/lib/rdkafka/admin/delete_groups_handle.rb +10 -5
- data/lib/rdkafka/admin/delete_groups_report.rb +3 -0
- data/lib/rdkafka/admin/delete_topic_handle.rb +8 -5
- data/lib/rdkafka/admin/delete_topic_report.rb +3 -0
- data/lib/rdkafka/admin/describe_acl_handle.rb +9 -6
- data/lib/rdkafka/admin/describe_acl_report.rb +5 -3
- data/lib/rdkafka/admin/describe_configs_handle.rb +7 -4
- data/lib/rdkafka/admin/describe_configs_report.rb +7 -1
- data/lib/rdkafka/admin/incremental_alter_configs_handle.rb +7 -4
- data/lib/rdkafka/admin/incremental_alter_configs_report.rb +7 -1
- data/lib/rdkafka/admin.rb +194 -132
- data/lib/rdkafka/bindings.rb +155 -107
- data/lib/rdkafka/callbacks.rb +81 -21
- data/lib/rdkafka/config.rb +36 -24
- data/lib/rdkafka/consumer/headers.rb +3 -2
- data/lib/rdkafka/consumer/message.rb +12 -11
- data/lib/rdkafka/consumer/partition.rb +8 -4
- data/lib/rdkafka/consumer/topic_partition_list.rb +18 -18
- data/lib/rdkafka/consumer.rb +247 -42
- data/lib/rdkafka/defaults.rb +106 -0
- data/lib/rdkafka/error.rb +28 -13
- data/lib/rdkafka/helpers/oauth.rb +11 -6
- data/lib/rdkafka/helpers/time.rb +5 -0
- data/lib/rdkafka/metadata.rb +45 -21
- data/lib/rdkafka/native_kafka.rb +89 -4
- data/lib/rdkafka/producer/delivery_handle.rb +5 -5
- data/lib/rdkafka/producer/delivery_report.rb +8 -4
- data/lib/rdkafka/producer/partitions_count_cache.rb +29 -19
- data/lib/rdkafka/producer.rb +165 -79
- data/lib/rdkafka/version.rb +6 -3
- data/lib/rdkafka.rb +1 -0
- data/package-lock.json +331 -0
- data/package.json +9 -0
- data/rdkafka.gemspec +39 -47
- data/renovate.json +22 -8
- metadata +7 -86
|
@@ -9,7 +9,7 @@ module Rdkafka
|
|
|
9
9
|
# @param data [Hash{String => nil,Partition}] The topic and partition data or nil to create an empty list
|
|
10
10
|
#
|
|
11
11
|
# @return [TopicPartitionList]
|
|
12
|
-
def initialize(data=nil)
|
|
12
|
+
def initialize(data = nil)
|
|
13
13
|
@data = data || {}
|
|
14
14
|
end
|
|
15
15
|
|
|
@@ -18,10 +18,10 @@ module Rdkafka
|
|
|
18
18
|
def count
|
|
19
19
|
i = 0
|
|
20
20
|
@data.each do |_topic, partitions|
|
|
21
|
-
if partitions
|
|
22
|
-
|
|
21
|
+
i += if partitions
|
|
22
|
+
partitions.count
|
|
23
23
|
else
|
|
24
|
-
|
|
24
|
+
1
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
i
|
|
@@ -49,15 +49,14 @@ module Rdkafka
|
|
|
49
49
|
#
|
|
50
50
|
# @example Add a topic with all topics up to a count
|
|
51
51
|
# tpl.add_topic("topic", 9)
|
|
52
|
-
|
|
53
|
-
def add_topic(topic, partitions=nil)
|
|
52
|
+
def add_topic(topic, partitions = nil)
|
|
54
53
|
if partitions.nil?
|
|
55
54
|
@data[topic.to_s] = nil
|
|
56
55
|
else
|
|
57
56
|
if partitions.is_a? Integer
|
|
58
57
|
partitions = (0..partitions - 1)
|
|
59
58
|
end
|
|
60
|
-
@data[topic.to_s] = partitions.map { |p| Partition.new(p, nil,
|
|
59
|
+
@data[topic.to_s] = partitions.map { |p| Partition.new(p, nil, Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR) }
|
|
61
60
|
end
|
|
62
61
|
end
|
|
63
62
|
|
|
@@ -65,10 +64,8 @@ module Rdkafka
|
|
|
65
64
|
# Calling this method multiple times for the same topic will overwrite the previous configuraton.
|
|
66
65
|
#
|
|
67
66
|
# @param topic [String] The topic's name
|
|
68
|
-
# @param partitions_with_offsets [Hash
|
|
69
|
-
#
|
|
70
|
-
# and metadata (if any)
|
|
71
|
-
#
|
|
67
|
+
# @param partitions_with_offsets [Hash{Integer => Integer}, Array<Consumer::Partition>] The topic's
|
|
68
|
+
# partitions and offsets (Hash) or partitions with offsets and metadata (Array)
|
|
72
69
|
# @return [nil]
|
|
73
70
|
def add_topic_and_partitions_with_offsets(topic, partitions_with_offsets)
|
|
74
71
|
@data[topic.to_s] = partitions_with_offsets.map do |p, o|
|
|
@@ -89,8 +86,11 @@ module Rdkafka
|
|
|
89
86
|
"<TopicPartitionList: #{to_h}>"
|
|
90
87
|
end
|
|
91
88
|
|
|
89
|
+
# Check equality with another TopicPartitionList
|
|
90
|
+
# @param other [TopicPartitionList] object to compare with
|
|
91
|
+
# @return [Boolean]
|
|
92
92
|
def ==(other)
|
|
93
|
-
|
|
93
|
+
to_h == other.to_h
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
# Create a new topic partition list based of a native one.
|
|
@@ -109,15 +109,15 @@ module Rdkafka
|
|
|
109
109
|
native_tpl[:cnt].times do |i|
|
|
110
110
|
ptr = native_tpl[:elems] + (i * Rdkafka::Bindings::TopicPartition.size)
|
|
111
111
|
elem = Rdkafka::Bindings::TopicPartition.new(ptr)
|
|
112
|
-
if elem[:partition] ==
|
|
112
|
+
if elem[:partition] == Rdkafka::Bindings::RD_KAFKA_PARTITION_UA
|
|
113
113
|
data[elem[:topic]] = nil
|
|
114
114
|
else
|
|
115
115
|
partitions = data[elem[:topic]] || []
|
|
116
116
|
offset = if elem[:offset] == Rdkafka::Bindings::RD_KAFKA_OFFSET_INVALID
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
nil
|
|
118
|
+
else
|
|
119
|
+
elem[:offset]
|
|
120
|
+
end
|
|
121
121
|
partition = Partition.new(elem[:partition], offset, elem[:err])
|
|
122
122
|
partitions.push(partition)
|
|
123
123
|
data[elem[:topic]] = partitions
|
|
@@ -161,7 +161,7 @@ module Rdkafka
|
|
|
161
161
|
Rdkafka::Bindings.rd_kafka_topic_partition_list_add(
|
|
162
162
|
tpl,
|
|
163
163
|
topic,
|
|
164
|
-
|
|
164
|
+
Rdkafka::Bindings::RD_KAFKA_PARTITION_UA
|
|
165
165
|
)
|
|
166
166
|
end
|
|
167
167
|
end
|
data/lib/rdkafka/consumer.rb
CHANGED
|
@@ -16,6 +16,7 @@ module Rdkafka
|
|
|
16
16
|
include Helpers::OAuth
|
|
17
17
|
|
|
18
18
|
# @private
|
|
19
|
+
# @param native_kafka [NativeKafka] wrapper around the native Kafka consumer handle
|
|
19
20
|
def initialize(native_kafka)
|
|
20
21
|
@native_kafka = native_kafka
|
|
21
22
|
end
|
|
@@ -33,6 +34,146 @@ module Rdkafka
|
|
|
33
34
|
end
|
|
34
35
|
end
|
|
35
36
|
|
|
37
|
+
# Enable IO event notifications for fiber scheduler integration
|
|
38
|
+
# When the consumer queue has messages, librdkafka will write to your FD
|
|
39
|
+
#
|
|
40
|
+
# @param fd [Integer] file descriptor to signal (from IO.pipe or eventfd)
|
|
41
|
+
# @param payload [String] data to write to fd (default: "\x01")
|
|
42
|
+
# @return [nil]
|
|
43
|
+
# @raise [ClosedInnerError] when the consumer is closed
|
|
44
|
+
#
|
|
45
|
+
# @example Using with fiber scheduler
|
|
46
|
+
# consumer = config.consumer
|
|
47
|
+
# consumer.subscribe("topic")
|
|
48
|
+
#
|
|
49
|
+
# # Create notification FD
|
|
50
|
+
# signal_r, signal_w = IO.pipe
|
|
51
|
+
#
|
|
52
|
+
# # Enable librdkafka to signal when messages arrive
|
|
53
|
+
# consumer.enable_queue_io_events(signal_w.fileno)
|
|
54
|
+
#
|
|
55
|
+
# # Monitor with select/poll
|
|
56
|
+
# loop do
|
|
57
|
+
# readable, = IO.select([signal_r], nil, nil, timeout)
|
|
58
|
+
# if readable
|
|
59
|
+
# signal_r.read_nonblock(1024) rescue nil # Drain signal
|
|
60
|
+
# while msg = consumer.poll(0)
|
|
61
|
+
# process(msg)
|
|
62
|
+
# end
|
|
63
|
+
# end
|
|
64
|
+
# end
|
|
65
|
+
def enable_queue_io_events(fd, payload = "\x01")
|
|
66
|
+
@native_kafka.enable_main_queue_io_events(fd, payload)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Enable IO event notifications for background events
|
|
70
|
+
# @param fd [Integer] file descriptor to signal (from IO.pipe or eventfd)
|
|
71
|
+
# @param payload [String] data to write to fd (default: "\x01")
|
|
72
|
+
# @return [nil]
|
|
73
|
+
# @raise [ClosedInnerError] when the consumer is closed
|
|
74
|
+
def enable_background_queue_io_events(fd, payload = "\x01")
|
|
75
|
+
@native_kafka.enable_background_queue_io_events(fd, payload)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Polls for events in a non-blocking loop, yielding the count after each iteration.
|
|
79
|
+
#
|
|
80
|
+
# This method processes events (stats, errors, etc.) in a single GVL/mutex session,
|
|
81
|
+
# which is more efficient than repeated individual polls. It uses non-blocking polls
|
|
82
|
+
# internally (no GVL release between polls).
|
|
83
|
+
#
|
|
84
|
+
# Yields the count of events processed after each poll iteration, allowing the caller
|
|
85
|
+
# to implement timeout or other termination logic by returning `:stop`.
|
|
86
|
+
#
|
|
87
|
+
# @yield [count] Called after each poll iteration
|
|
88
|
+
# @yieldparam count [Integer] Number of events processed in this iteration
|
|
89
|
+
# @yieldreturn [Symbol, Object] Return `:stop` to break the loop, any other value continues
|
|
90
|
+
# @return [nil]
|
|
91
|
+
# @raise [Rdkafka::ClosedConsumerError] if called on a closed consumer
|
|
92
|
+
#
|
|
93
|
+
# @note This method holds the inner lock until the queue is empty or `:stop` is returned.
|
|
94
|
+
# Other consumer operations will wait until this method returns.
|
|
95
|
+
# @note This method is thread-safe as it uses @native_kafka.with_inner synchronization
|
|
96
|
+
# @note Do NOT use this if `consumer_poll_set` was set to `true`
|
|
97
|
+
#
|
|
98
|
+
# @example Drain all pending events
|
|
99
|
+
# consumer.events_poll_nb_each { |_count| }
|
|
100
|
+
#
|
|
101
|
+
# @example With timeout control
|
|
102
|
+
# deadline = monotonic_now + timeout_ms
|
|
103
|
+
# consumer.events_poll_nb_each do |_count|
|
|
104
|
+
# :stop if monotonic_now >= deadline
|
|
105
|
+
# end
|
|
106
|
+
def events_poll_nb_each
|
|
107
|
+
closed_consumer_check(__method__)
|
|
108
|
+
|
|
109
|
+
@native_kafka.with_inner do |inner|
|
|
110
|
+
loop do
|
|
111
|
+
count = Rdkafka::Bindings.rd_kafka_poll_nb(inner, 0)
|
|
112
|
+
break if count.zero?
|
|
113
|
+
break if yield(count) == :stop
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Polls for messages in a non-blocking loop, yielding each message to the caller.
|
|
119
|
+
#
|
|
120
|
+
# This method processes messages in a single GVL/mutex session until the queue is empty
|
|
121
|
+
# or the caller returns `:stop`. It handles the message pointer lifecycle internally,
|
|
122
|
+
# ensuring proper cleanup via `rd_kafka_message_destroy`.
|
|
123
|
+
#
|
|
124
|
+
# @yield [message] Called for each message received
|
|
125
|
+
# @yieldparam message [Consumer::Message] The received message
|
|
126
|
+
# @yieldreturn [Symbol, Object] Return `:stop` to break the loop, any other value continues
|
|
127
|
+
# @return [nil]
|
|
128
|
+
# @raise [Rdkafka::ClosedConsumerError] if called on a closed consumer
|
|
129
|
+
# @raise [Rdkafka::RdkafkaError] if a Kafka error occurs while polling
|
|
130
|
+
#
|
|
131
|
+
# @note This method uses `rd_kafka_consumer_poll` to fetch messages, unlike
|
|
132
|
+
# `events_poll_nb_each` which uses `rd_kafka_poll` for event callbacks (delivery reports,
|
|
133
|
+
# statistics, etc.). For consumers, use this method to receive messages and
|
|
134
|
+
# `events_poll_nb_each` for processing background events.
|
|
135
|
+
# @note This method holds the inner lock for the duration. Other consumer operations
|
|
136
|
+
# will wait until this method returns.
|
|
137
|
+
# @note Timeout/max_messages logic should be implemented by the caller
|
|
138
|
+
#
|
|
139
|
+
# @example Process messages until queue is empty
|
|
140
|
+
# consumer.poll_nb_each do |message|
|
|
141
|
+
# process(message)
|
|
142
|
+
# end
|
|
143
|
+
#
|
|
144
|
+
# @example Process with early termination
|
|
145
|
+
# count = 0
|
|
146
|
+
# consumer.poll_nb_each do |message|
|
|
147
|
+
# process(message)
|
|
148
|
+
# count += 1
|
|
149
|
+
# :stop if count >= 10
|
|
150
|
+
# end
|
|
151
|
+
def poll_nb_each
|
|
152
|
+
closed_consumer_check(__method__)
|
|
153
|
+
|
|
154
|
+
@native_kafka.with_inner do |inner|
|
|
155
|
+
loop do
|
|
156
|
+
message_ptr = Rdkafka::Bindings.rd_kafka_consumer_poll_nb(inner, 0)
|
|
157
|
+
break if message_ptr.null?
|
|
158
|
+
|
|
159
|
+
begin
|
|
160
|
+
native_message = Rdkafka::Bindings::Message.new(message_ptr)
|
|
161
|
+
|
|
162
|
+
if native_message[:err] != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
163
|
+
raise Rdkafka::RdkafkaError.new(native_message[:err])
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
result = yield Consumer::Message.new(native_message)
|
|
167
|
+
break if result == :stop
|
|
168
|
+
ensure
|
|
169
|
+
Rdkafka::Bindings.rd_kafka_message_destroy(message_ptr)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# @return [Proc] finalizer proc for closing the consumer
|
|
176
|
+
# @private
|
|
36
177
|
def finalizer
|
|
37
178
|
->(_) { close }
|
|
38
179
|
end
|
|
@@ -67,15 +208,15 @@ module Rdkafka
|
|
|
67
208
|
tpl = Rdkafka::Bindings.rd_kafka_topic_partition_list_new(topics.length)
|
|
68
209
|
|
|
69
210
|
topics.each do |topic|
|
|
70
|
-
Rdkafka::Bindings.rd_kafka_topic_partition_list_add(tpl, topic,
|
|
211
|
+
Rdkafka::Bindings.rd_kafka_topic_partition_list_add(tpl, topic, Rdkafka::Bindings::RD_KAFKA_PARTITION_UA)
|
|
71
212
|
end
|
|
72
213
|
|
|
73
214
|
# Subscribe to topic partition list and check this was successful
|
|
74
215
|
response = @native_kafka.with_inner do |inner|
|
|
75
216
|
Rdkafka::Bindings.rd_kafka_subscribe(inner, tpl)
|
|
76
217
|
end
|
|
77
|
-
if response !=
|
|
78
|
-
raise Rdkafka::RdkafkaError.new(response, "Error subscribing to '#{topics.join(
|
|
218
|
+
if response != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
219
|
+
raise Rdkafka::RdkafkaError.new(response, "Error subscribing to '#{topics.join(", ")}'")
|
|
79
220
|
end
|
|
80
221
|
ensure
|
|
81
222
|
Rdkafka::Bindings.rd_kafka_topic_partition_list_destroy(tpl) unless tpl.nil?
|
|
@@ -91,7 +232,7 @@ module Rdkafka
|
|
|
91
232
|
response = @native_kafka.with_inner do |inner|
|
|
92
233
|
Rdkafka::Bindings.rd_kafka_unsubscribe(inner)
|
|
93
234
|
end
|
|
94
|
-
if response !=
|
|
235
|
+
if response != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
95
236
|
raise Rdkafka::RdkafkaError.new(response)
|
|
96
237
|
end
|
|
97
238
|
end
|
|
@@ -115,7 +256,7 @@ module Rdkafka
|
|
|
115
256
|
Rdkafka::Bindings.rd_kafka_pause_partitions(inner, tpl)
|
|
116
257
|
end
|
|
117
258
|
|
|
118
|
-
if response !=
|
|
259
|
+
if response != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
119
260
|
list = TopicPartitionList.from_native_tpl(tpl)
|
|
120
261
|
raise Rdkafka::RdkafkaTopicPartitionListError.new(response, list, "Error pausing '#{list.to_h}'")
|
|
121
262
|
end
|
|
@@ -142,7 +283,7 @@ module Rdkafka
|
|
|
142
283
|
response = @native_kafka.with_inner do |inner|
|
|
143
284
|
Rdkafka::Bindings.rd_kafka_resume_partitions(inner, tpl)
|
|
144
285
|
end
|
|
145
|
-
if response !=
|
|
286
|
+
if response != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
146
287
|
raise Rdkafka::RdkafkaError.new(response, "Error resume '#{list.to_h}'")
|
|
147
288
|
end
|
|
148
289
|
ensure
|
|
@@ -162,7 +303,7 @@ module Rdkafka
|
|
|
162
303
|
Rdkafka::Bindings.rd_kafka_subscription(inner, ptr)
|
|
163
304
|
end
|
|
164
305
|
|
|
165
|
-
if response !=
|
|
306
|
+
if response != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
166
307
|
raise Rdkafka::RdkafkaError.new(response)
|
|
167
308
|
end
|
|
168
309
|
|
|
@@ -192,7 +333,7 @@ module Rdkafka
|
|
|
192
333
|
response = @native_kafka.with_inner do |inner|
|
|
193
334
|
Rdkafka::Bindings.rd_kafka_assign(inner, tpl)
|
|
194
335
|
end
|
|
195
|
-
if response !=
|
|
336
|
+
if response != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
196
337
|
raise Rdkafka::RdkafkaError.new(response, "Error assigning '#{list.to_h}'")
|
|
197
338
|
end
|
|
198
339
|
ensure
|
|
@@ -211,7 +352,7 @@ module Rdkafka
|
|
|
211
352
|
response = @native_kafka.with_inner do |inner|
|
|
212
353
|
Rdkafka::Bindings.rd_kafka_assignment(inner, ptr)
|
|
213
354
|
end
|
|
214
|
-
if response !=
|
|
355
|
+
if response != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
215
356
|
raise Rdkafka::RdkafkaError.new(response)
|
|
216
357
|
end
|
|
217
358
|
|
|
@@ -225,7 +366,7 @@ module Rdkafka
|
|
|
225
366
|
end
|
|
226
367
|
end
|
|
227
368
|
ensure
|
|
228
|
-
ptr
|
|
369
|
+
ptr&.free
|
|
229
370
|
end
|
|
230
371
|
|
|
231
372
|
# @return [Boolean] true if our current assignment has been lost involuntarily.
|
|
@@ -246,7 +387,7 @@ module Rdkafka
|
|
|
246
387
|
# @param timeout_ms [Integer] The timeout for fetching this information.
|
|
247
388
|
# @return [TopicPartitionList]
|
|
248
389
|
# @raise [RdkafkaError] When getting the committed positions fails.
|
|
249
|
-
def committed(list=nil, timeout_ms=
|
|
390
|
+
def committed(list = nil, timeout_ms = Defaults::CONSUMER_COMMITTED_TIMEOUT_MS)
|
|
250
391
|
closed_consumer_check(__method__)
|
|
251
392
|
|
|
252
393
|
if list.nil?
|
|
@@ -261,7 +402,7 @@ module Rdkafka
|
|
|
261
402
|
response = @native_kafka.with_inner do |inner|
|
|
262
403
|
Rdkafka::Bindings.rd_kafka_committed(inner, tpl, timeout_ms)
|
|
263
404
|
end
|
|
264
|
-
if response !=
|
|
405
|
+
if response != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
265
406
|
raise Rdkafka::RdkafkaError.new(response)
|
|
266
407
|
end
|
|
267
408
|
TopicPartitionList.from_native_tpl(tpl)
|
|
@@ -278,7 +419,7 @@ module Rdkafka
|
|
|
278
419
|
# @return [TopicPartitionList]
|
|
279
420
|
#
|
|
280
421
|
# @raise [RdkafkaError] When getting the positions fails.
|
|
281
|
-
def position(list=nil)
|
|
422
|
+
def position(list = nil)
|
|
282
423
|
if list.nil?
|
|
283
424
|
list = assignment
|
|
284
425
|
elsif !list.is_a?(TopicPartitionList)
|
|
@@ -291,7 +432,7 @@ module Rdkafka
|
|
|
291
432
|
Rdkafka::Bindings.rd_kafka_position(inner, tpl)
|
|
292
433
|
end
|
|
293
434
|
|
|
294
|
-
if response !=
|
|
435
|
+
if response != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
295
436
|
raise Rdkafka::RdkafkaError.new(response)
|
|
296
437
|
end
|
|
297
438
|
|
|
@@ -305,7 +446,7 @@ module Rdkafka
|
|
|
305
446
|
# @param timeout_ms [Integer] The timeout for querying the broker
|
|
306
447
|
# @return [Integer] The low and high watermark
|
|
307
448
|
# @raise [RdkafkaError] When querying the broker fails.
|
|
308
|
-
def query_watermark_offsets(topic, partition, timeout_ms=
|
|
449
|
+
def query_watermark_offsets(topic, partition, timeout_ms = Defaults::CONSUMER_QUERY_WATERMARK_TIMEOUT_MS)
|
|
309
450
|
closed_consumer_check(__method__)
|
|
310
451
|
|
|
311
452
|
low = FFI::MemoryPointer.new(:int64, 1)
|
|
@@ -318,17 +459,17 @@ module Rdkafka
|
|
|
318
459
|
partition,
|
|
319
460
|
low,
|
|
320
461
|
high,
|
|
321
|
-
timeout_ms
|
|
462
|
+
timeout_ms
|
|
322
463
|
)
|
|
323
464
|
end
|
|
324
|
-
if response !=
|
|
465
|
+
if response != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
325
466
|
raise Rdkafka::RdkafkaError.new(response, "Error querying watermark offsets for partition #{partition} of #{topic}")
|
|
326
467
|
end
|
|
327
468
|
|
|
328
|
-
|
|
469
|
+
[low.read_array_of_int64(1).first, high.read_array_of_int64(1).first]
|
|
329
470
|
ensure
|
|
330
|
-
low
|
|
331
|
-
high
|
|
471
|
+
low&.free
|
|
472
|
+
high&.free
|
|
332
473
|
end
|
|
333
474
|
|
|
334
475
|
# Calculate the consumer lag per partition for the provided topic partition list.
|
|
@@ -338,10 +479,10 @@ module Rdkafka
|
|
|
338
479
|
#
|
|
339
480
|
# @param topic_partition_list [TopicPartitionList] The list to calculate lag for.
|
|
340
481
|
# @param watermark_timeout_ms [Integer] The timeout for each query watermark call.
|
|
341
|
-
# @return [Hash
|
|
482
|
+
# @return [Hash{String => Hash{Integer => Integer}}] A hash containing all topics with the lag
|
|
342
483
|
# per partition
|
|
343
484
|
# @raise [RdkafkaError] When querying the broker fails.
|
|
344
|
-
def lag(topic_partition_list, watermark_timeout_ms=
|
|
485
|
+
def lag(topic_partition_list, watermark_timeout_ms = Defaults::CONSUMER_LAG_TIMEOUT_MS)
|
|
345
486
|
out = {}
|
|
346
487
|
|
|
347
488
|
topic_partition_list.to_h.each do |topic, partitions|
|
|
@@ -409,7 +550,7 @@ module Rdkafka
|
|
|
409
550
|
)
|
|
410
551
|
end
|
|
411
552
|
|
|
412
|
-
if response !=
|
|
553
|
+
if response != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
413
554
|
raise Rdkafka::RdkafkaError.new(response)
|
|
414
555
|
end
|
|
415
556
|
ensure
|
|
@@ -451,9 +592,9 @@ module Rdkafka
|
|
|
451
592
|
native_topic,
|
|
452
593
|
partition,
|
|
453
594
|
offset,
|
|
454
|
-
|
|
595
|
+
Defaults::CONSUMER_SEEK_TIMEOUT_MS
|
|
455
596
|
)
|
|
456
|
-
if response !=
|
|
597
|
+
if response != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
457
598
|
raise Rdkafka::RdkafkaError.new(response)
|
|
458
599
|
end
|
|
459
600
|
ensure
|
|
@@ -465,11 +606,10 @@ module Rdkafka
|
|
|
465
606
|
# Lookup offset for the given partitions by timestamp.
|
|
466
607
|
#
|
|
467
608
|
# @param list [TopicPartitionList] The TopicPartitionList with timestamps instead of offsets
|
|
468
|
-
#
|
|
609
|
+
# @param timeout_ms [Integer] timeout in milliseconds for the operation
|
|
469
610
|
# @return [TopicPartitionList]
|
|
470
|
-
#
|
|
471
611
|
# @raise [RdKafkaError] When the OffsetForTimes lookup fails
|
|
472
|
-
def offsets_for_times(list, timeout_ms =
|
|
612
|
+
def offsets_for_times(list, timeout_ms = Defaults::CONSUMER_OFFSETS_FOR_TIMES_TIMEOUT_MS)
|
|
473
613
|
closed_consumer_check(__method__)
|
|
474
614
|
|
|
475
615
|
if !list.is_a?(TopicPartitionList)
|
|
@@ -486,7 +626,7 @@ module Rdkafka
|
|
|
486
626
|
)
|
|
487
627
|
end
|
|
488
628
|
|
|
489
|
-
if response !=
|
|
629
|
+
if response != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
490
630
|
raise Rdkafka::RdkafkaError.new(response)
|
|
491
631
|
end
|
|
492
632
|
|
|
@@ -508,20 +648,20 @@ module Rdkafka
|
|
|
508
648
|
# @param async [Boolean] Whether to commit async or wait for the commit to finish
|
|
509
649
|
# @return [nil]
|
|
510
650
|
# @raise [RdkafkaError] When committing fails
|
|
511
|
-
def commit(list=nil, async=false)
|
|
651
|
+
def commit(list = nil, async = false)
|
|
512
652
|
closed_consumer_check(__method__)
|
|
513
653
|
|
|
514
654
|
if !list.nil? && !list.is_a?(TopicPartitionList)
|
|
515
655
|
raise TypeError.new("list has to be nil or a TopicPartitionList")
|
|
516
656
|
end
|
|
517
657
|
|
|
518
|
-
tpl = list
|
|
658
|
+
tpl = list&.to_native_tpl
|
|
519
659
|
|
|
520
660
|
begin
|
|
521
661
|
response = @native_kafka.with_inner do |inner|
|
|
522
662
|
Rdkafka::Bindings.rd_kafka_commit(inner, tpl, async)
|
|
523
663
|
end
|
|
524
|
-
if response !=
|
|
664
|
+
if response != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
525
665
|
raise Rdkafka::RdkafkaError.new(response)
|
|
526
666
|
end
|
|
527
667
|
ensure
|
|
@@ -546,7 +686,48 @@ module Rdkafka
|
|
|
546
686
|
# Create struct wrapper
|
|
547
687
|
native_message = Rdkafka::Bindings::Message.new(message_ptr)
|
|
548
688
|
# Raise error if needed
|
|
549
|
-
if native_message[:err] !=
|
|
689
|
+
if native_message[:err] != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
690
|
+
raise Rdkafka::RdkafkaError.new(native_message[:err])
|
|
691
|
+
end
|
|
692
|
+
# Create a message to pass out
|
|
693
|
+
Rdkafka::Consumer::Message.new(native_message)
|
|
694
|
+
end
|
|
695
|
+
ensure
|
|
696
|
+
# Clean up rdkafka message if there is one
|
|
697
|
+
if message_ptr && !message_ptr.null?
|
|
698
|
+
Rdkafka::Bindings.rd_kafka_message_destroy(message_ptr)
|
|
699
|
+
end
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
# Poll for the next message without releasing the GVL (Global VM Lock).
|
|
703
|
+
#
|
|
704
|
+
# This is more efficient than regular polling for non-blocking poll(0) calls,
|
|
705
|
+
# particularly useful in fiber scheduler contexts where GVL release/reacquire
|
|
706
|
+
# overhead is wasteful since we don't expect to wait.
|
|
707
|
+
#
|
|
708
|
+
# @param timeout_ms [Integer] Timeout of this poll (default: 0 for non-blocking)
|
|
709
|
+
# @return [Message, nil] A message or nil if there was no new message within the timeout
|
|
710
|
+
# @raise [RdkafkaError] When polling fails
|
|
711
|
+
#
|
|
712
|
+
# @example Using with fiber scheduler
|
|
713
|
+
# # After receiving IO notification that messages are available
|
|
714
|
+
# while msg = consumer.poll_nb
|
|
715
|
+
# process(msg)
|
|
716
|
+
# end
|
|
717
|
+
def poll_nb(timeout_ms = 0)
|
|
718
|
+
closed_consumer_check(__method__)
|
|
719
|
+
|
|
720
|
+
message_ptr = @native_kafka.with_inner do |inner|
|
|
721
|
+
Rdkafka::Bindings.rd_kafka_consumer_poll_nb(inner, timeout_ms)
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
if message_ptr.null?
|
|
725
|
+
nil
|
|
726
|
+
else
|
|
727
|
+
# Create struct wrapper
|
|
728
|
+
native_message = Rdkafka::Bindings::Message.new(message_ptr)
|
|
729
|
+
# Raise error if needed
|
|
730
|
+
if native_message[:err] != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
550
731
|
raise Rdkafka::RdkafkaError.new(native_message[:err])
|
|
551
732
|
end
|
|
552
733
|
# Create a message to pass out
|
|
@@ -579,37 +760,58 @@ module Rdkafka
|
|
|
579
760
|
# @note This method technically should be called `#poll` and the current `#poll` should be
|
|
580
761
|
# called `#consumer_poll` though we keep the current naming convention to make it backward
|
|
581
762
|
# compatible.
|
|
582
|
-
def events_poll(timeout_ms =
|
|
763
|
+
def events_poll(timeout_ms = Defaults::CONSUMER_EVENTS_POLL_TIMEOUT_MS)
|
|
583
764
|
@native_kafka.with_inner do |inner|
|
|
584
765
|
Rdkafka::Bindings.rd_kafka_poll(inner, timeout_ms)
|
|
585
766
|
end
|
|
586
767
|
end
|
|
587
768
|
|
|
769
|
+
# Polls the main rdkafka queue without releasing the GVL (Global VM Lock).
|
|
770
|
+
#
|
|
771
|
+
# This is more efficient than regular events_poll for non-blocking poll(0) calls,
|
|
772
|
+
# particularly useful in fiber scheduler contexts where GVL release/reacquire
|
|
773
|
+
# overhead is wasteful since we don't expect to wait.
|
|
774
|
+
#
|
|
775
|
+
# @param timeout_ms [Integer] poll timeout (default: 0 for non-blocking)
|
|
776
|
+
# @return [Integer] the number of events served
|
|
777
|
+
#
|
|
778
|
+
# @see #events_poll for more details on when to use this method
|
|
779
|
+
def events_poll_nb(timeout_ms = 0)
|
|
780
|
+
@native_kafka.with_inner do |inner|
|
|
781
|
+
Rdkafka::Bindings.rd_kafka_poll_nb(inner, timeout_ms)
|
|
782
|
+
end
|
|
783
|
+
end
|
|
784
|
+
|
|
588
785
|
# Poll for new messages and yield for each received one. Iteration
|
|
589
786
|
# will end when the consumer is closed.
|
|
590
787
|
#
|
|
591
788
|
# If `enable.partition.eof` is turned on in the config this will raise an error when an eof is
|
|
592
789
|
# reached, so you probably want to disable that when using this method of iteration.
|
|
593
790
|
#
|
|
791
|
+
# @param timeout_ms [Integer] Timeout for each poll iteration
|
|
594
792
|
# @yieldparam message [Message] Received message
|
|
595
793
|
# @return [nil]
|
|
596
794
|
# @raise [RdkafkaError] When polling fails
|
|
597
|
-
def each
|
|
795
|
+
def each(timeout_ms: Defaults::CONSUMER_POLL_TIMEOUT_MS)
|
|
598
796
|
loop do
|
|
599
|
-
message = poll(
|
|
797
|
+
message = poll(timeout_ms)
|
|
600
798
|
if message
|
|
601
799
|
yield(message)
|
|
800
|
+
elsif closed?
|
|
801
|
+
break
|
|
602
802
|
else
|
|
603
|
-
|
|
604
|
-
break
|
|
605
|
-
else
|
|
606
|
-
next
|
|
607
|
-
end
|
|
803
|
+
next
|
|
608
804
|
end
|
|
609
805
|
end
|
|
610
806
|
end
|
|
611
807
|
|
|
612
|
-
#
|
|
808
|
+
# @deprecated This method has been removed due to data consistency concerns
|
|
809
|
+
# @param max_items [Integer] unused
|
|
810
|
+
# @param bytes_threshold [Numeric] unused
|
|
811
|
+
# @param timeout_ms [Integer] unused
|
|
812
|
+
# @param yield_on_error [Boolean] unused
|
|
813
|
+
# @param block [Proc] unused block
|
|
814
|
+
# @raise [NotImplementedError] Always raises as this method is no longer supported
|
|
613
815
|
def each_batch(max_items: 100, bytes_threshold: Float::INFINITY, timeout_ms: 250, yield_on_error: false, &block)
|
|
614
816
|
raise NotImplementedError, <<~ERROR
|
|
615
817
|
`each_batch` has been removed due to data consistency concerns.
|
|
@@ -646,6 +848,9 @@ module Rdkafka
|
|
|
646
848
|
|
|
647
849
|
private
|
|
648
850
|
|
|
851
|
+
# Checks if the consumer is closed and raises an error if so
|
|
852
|
+
# @param method [Symbol] name of the calling method for error context
|
|
853
|
+
# @raise [ClosedConsumerError] when the consumer is closed
|
|
649
854
|
def closed_consumer_check(method)
|
|
650
855
|
raise Rdkafka::ClosedConsumerError.new(method) if closed?
|
|
651
856
|
end
|