rdkafka 0.25.0-aarch64-linux-gnu → 0.26.0-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 +17 -0
- data/Gemfile +5 -6
- data/Gemfile.lint +14 -0
- data/Gemfile.lint.lock +123 -0
- data/README.md +2 -1
- data/Rakefile +21 -21
- data/bin/verify_kafka_warnings +2 -0
- data/docker-compose-ssl.yml +2 -2
- data/docker-compose.yml +2 -2
- data/ext/librdkafka.so +0 -0
- data/lib/rdkafka/admin/acl_binding_result.rb +4 -4
- data/lib/rdkafka/admin/create_acl_handle.rb +4 -4
- data/lib/rdkafka/admin/create_acl_report.rb +0 -2
- data/lib/rdkafka/admin/create_partitions_handle.rb +5 -5
- data/lib/rdkafka/admin/create_topic_handle.rb +5 -5
- data/lib/rdkafka/admin/delete_acl_handle.rb +6 -6
- data/lib/rdkafka/admin/delete_acl_report.rb +2 -3
- data/lib/rdkafka/admin/delete_groups_handle.rb +5 -5
- data/lib/rdkafka/admin/delete_topic_handle.rb +5 -5
- data/lib/rdkafka/admin/describe_acl_handle.rb +6 -6
- data/lib/rdkafka/admin/describe_acl_report.rb +2 -3
- data/lib/rdkafka/admin/describe_configs_handle.rb +4 -4
- data/lib/rdkafka/admin/describe_configs_report.rb +1 -1
- data/lib/rdkafka/admin/incremental_alter_configs_handle.rb +4 -4
- data/lib/rdkafka/admin/incremental_alter_configs_report.rb +1 -1
- data/lib/rdkafka/admin/list_offsets_handle.rb +36 -0
- data/lib/rdkafka/admin/list_offsets_report.rb +51 -0
- data/lib/rdkafka/admin.rb +189 -24
- data/lib/rdkafka/bindings.rb +121 -84
- data/lib/rdkafka/callbacks.rb +53 -10
- data/lib/rdkafka/config.rb +20 -20
- data/lib/rdkafka/consumer/message.rb +5 -8
- data/lib/rdkafka/consumer/partition.rb +2 -2
- data/lib/rdkafka/consumer/topic_partition_list.rb +10 -10
- data/lib/rdkafka/consumer.rb +208 -18
- data/lib/rdkafka/error.rb +25 -14
- data/lib/rdkafka/helpers/oauth.rb +0 -1
- data/lib/rdkafka/helpers/time.rb +5 -0
- data/lib/rdkafka/metadata.rb +16 -16
- data/lib/rdkafka/native_kafka.rb +63 -2
- data/lib/rdkafka/producer/delivery_handle.rb +5 -5
- data/lib/rdkafka/producer/delivery_report.rb +1 -1
- data/lib/rdkafka/producer/partitions_count_cache.rb +6 -6
- data/lib/rdkafka/producer.rb +117 -57
- data/lib/rdkafka/version.rb +3 -3
- data/lib/rdkafka.rb +2 -0
- data/package-lock.json +331 -0
- data/package.json +9 -0
- data/rdkafka.gemspec +39 -40
- data/renovate.json +21 -0
- metadata +8 -2
|
@@ -53,13 +53,11 @@ module Rdkafka
|
|
|
53
53
|
# Set timestamp
|
|
54
54
|
raw_timestamp = Rdkafka::Bindings.rd_kafka_message_timestamp(native_message, nil)
|
|
55
55
|
@timestamp = if raw_timestamp && raw_timestamp > -1
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
nil
|
|
62
|
-
end
|
|
56
|
+
# Calculate seconds and microseconds
|
|
57
|
+
seconds = raw_timestamp / 1000
|
|
58
|
+
milliseconds = (raw_timestamp - seconds * 1000) * 1000
|
|
59
|
+
Time.at(seconds, milliseconds)
|
|
60
|
+
end
|
|
63
61
|
|
|
64
62
|
@headers = Headers.from_native(native_message)
|
|
65
63
|
end
|
|
@@ -84,7 +82,6 @@ module Rdkafka
|
|
|
84
82
|
string
|
|
85
83
|
end
|
|
86
84
|
end
|
|
87
|
-
|
|
88
85
|
end
|
|
89
86
|
end
|
|
90
87
|
end
|
|
@@ -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,7 +49,7 @@ 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
|
-
def add_topic(topic, partitions=nil)
|
|
52
|
+
def add_topic(topic, partitions = nil)
|
|
53
53
|
if partitions.nil?
|
|
54
54
|
@data[topic.to_s] = nil
|
|
55
55
|
else
|
|
@@ -90,7 +90,7 @@ module Rdkafka
|
|
|
90
90
|
# @param other [TopicPartitionList] object to compare with
|
|
91
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.
|
|
@@ -114,10 +114,10 @@ module Rdkafka
|
|
|
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
|
data/lib/rdkafka/consumer.rb
CHANGED
|
@@ -19,6 +19,9 @@ module Rdkafka
|
|
|
19
19
|
# @param native_kafka [NativeKafka] wrapper around the native Kafka consumer handle
|
|
20
20
|
def initialize(native_kafka)
|
|
21
21
|
@native_kafka = native_kafka
|
|
22
|
+
|
|
23
|
+
# Makes sure, that native kafka gets closed before it gets GCed by Ruby
|
|
24
|
+
ObjectSpace.define_finalizer(self, native_kafka.finalizer)
|
|
22
25
|
end
|
|
23
26
|
|
|
24
27
|
# Starts the native Kafka polling thread and kicks off the init polling
|
|
@@ -34,10 +37,142 @@ module Rdkafka
|
|
|
34
37
|
end
|
|
35
38
|
end
|
|
36
39
|
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
# Enable IO event notifications for fiber scheduler integration
|
|
41
|
+
# When the consumer queue has messages, librdkafka will write to your FD
|
|
42
|
+
#
|
|
43
|
+
# @param fd [Integer] file descriptor to signal (from IO.pipe or eventfd)
|
|
44
|
+
# @param payload [String] data to write to fd (default: "\x01")
|
|
45
|
+
# @return [nil]
|
|
46
|
+
# @raise [ClosedInnerError] when the consumer is closed
|
|
47
|
+
#
|
|
48
|
+
# @example Using with fiber scheduler
|
|
49
|
+
# consumer = config.consumer
|
|
50
|
+
# consumer.subscribe("topic")
|
|
51
|
+
#
|
|
52
|
+
# # Create notification FD
|
|
53
|
+
# signal_r, signal_w = IO.pipe
|
|
54
|
+
#
|
|
55
|
+
# # Enable librdkafka to signal when messages arrive
|
|
56
|
+
# consumer.enable_queue_io_events(signal_w.fileno)
|
|
57
|
+
#
|
|
58
|
+
# # Monitor with select/poll
|
|
59
|
+
# loop do
|
|
60
|
+
# readable, = IO.select([signal_r], nil, nil, timeout)
|
|
61
|
+
# if readable
|
|
62
|
+
# signal_r.read_nonblock(1024) rescue nil # Drain signal
|
|
63
|
+
# while msg = consumer.poll(0)
|
|
64
|
+
# process(msg)
|
|
65
|
+
# end
|
|
66
|
+
# end
|
|
67
|
+
# end
|
|
68
|
+
def enable_queue_io_events(fd, payload = "\x01")
|
|
69
|
+
@native_kafka.enable_main_queue_io_events(fd, payload)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Enable IO event notifications for background events
|
|
73
|
+
# @param fd [Integer] file descriptor to signal (from IO.pipe or eventfd)
|
|
74
|
+
# @param payload [String] data to write to fd (default: "\x01")
|
|
75
|
+
# @return [nil]
|
|
76
|
+
# @raise [ClosedInnerError] when the consumer is closed
|
|
77
|
+
def enable_background_queue_io_events(fd, payload = "\x01")
|
|
78
|
+
@native_kafka.enable_background_queue_io_events(fd, payload)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Polls for events in a non-blocking loop, yielding the count after each iteration.
|
|
82
|
+
#
|
|
83
|
+
# This method processes events (stats, errors, etc.) in a single GVL/mutex session,
|
|
84
|
+
# which is more efficient than repeated individual polls. It uses non-blocking polls
|
|
85
|
+
# internally (no GVL release between polls).
|
|
86
|
+
#
|
|
87
|
+
# Yields the count of events processed after each poll iteration, allowing the caller
|
|
88
|
+
# to implement timeout or other termination logic by returning `:stop`.
|
|
89
|
+
#
|
|
90
|
+
# @yield [count] Called after each poll iteration
|
|
91
|
+
# @yieldparam count [Integer] Number of events processed in this iteration
|
|
92
|
+
# @yieldreturn [Symbol, Object] Return `:stop` to break the loop, any other value continues
|
|
93
|
+
# @return [nil]
|
|
94
|
+
# @raise [Rdkafka::ClosedConsumerError] if called on a closed consumer
|
|
95
|
+
#
|
|
96
|
+
# @note This method holds the inner lock until the queue is empty or `:stop` is returned.
|
|
97
|
+
# Other consumer operations will wait until this method returns.
|
|
98
|
+
# @note This method is thread-safe as it uses @native_kafka.with_inner synchronization
|
|
99
|
+
# @note Do NOT use this if `consumer_poll_set` was set to `true`
|
|
100
|
+
#
|
|
101
|
+
# @example Drain all pending events
|
|
102
|
+
# consumer.events_poll_nb_each { |_count| }
|
|
103
|
+
#
|
|
104
|
+
# @example With timeout control
|
|
105
|
+
# deadline = monotonic_now + timeout_ms
|
|
106
|
+
# consumer.events_poll_nb_each do |_count|
|
|
107
|
+
# :stop if monotonic_now >= deadline
|
|
108
|
+
# end
|
|
109
|
+
def events_poll_nb_each
|
|
110
|
+
closed_consumer_check(__method__)
|
|
111
|
+
|
|
112
|
+
@native_kafka.with_inner do |inner|
|
|
113
|
+
loop do
|
|
114
|
+
count = Rdkafka::Bindings.rd_kafka_poll_nb(inner, 0)
|
|
115
|
+
break if count.zero?
|
|
116
|
+
break if yield(count) == :stop
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Polls for messages in a non-blocking loop, yielding each message to the caller.
|
|
122
|
+
#
|
|
123
|
+
# This method processes messages in a single GVL/mutex session until the queue is empty
|
|
124
|
+
# or the caller returns `:stop`. It handles the message pointer lifecycle internally,
|
|
125
|
+
# ensuring proper cleanup via `rd_kafka_message_destroy`.
|
|
126
|
+
#
|
|
127
|
+
# @yield [message] Called for each message received
|
|
128
|
+
# @yieldparam message [Consumer::Message] The received message
|
|
129
|
+
# @yieldreturn [Symbol, Object] Return `:stop` to break the loop, any other value continues
|
|
130
|
+
# @return [nil]
|
|
131
|
+
# @raise [Rdkafka::ClosedConsumerError] if called on a closed consumer
|
|
132
|
+
# @raise [Rdkafka::RdkafkaError] if a Kafka error occurs while polling
|
|
133
|
+
#
|
|
134
|
+
# @note This method uses `rd_kafka_consumer_poll` to fetch messages, unlike
|
|
135
|
+
# `events_poll_nb_each` which uses `rd_kafka_poll` for event callbacks (delivery reports,
|
|
136
|
+
# statistics, etc.). For consumers, use this method to receive messages and
|
|
137
|
+
# `events_poll_nb_each` for processing background events.
|
|
138
|
+
# @note This method holds the inner lock for the duration. Other consumer operations
|
|
139
|
+
# will wait until this method returns.
|
|
140
|
+
# @note Timeout/max_messages logic should be implemented by the caller
|
|
141
|
+
#
|
|
142
|
+
# @example Process messages until queue is empty
|
|
143
|
+
# consumer.poll_nb_each do |message|
|
|
144
|
+
# process(message)
|
|
145
|
+
# end
|
|
146
|
+
#
|
|
147
|
+
# @example Process with early termination
|
|
148
|
+
# count = 0
|
|
149
|
+
# consumer.poll_nb_each do |message|
|
|
150
|
+
# process(message)
|
|
151
|
+
# count += 1
|
|
152
|
+
# :stop if count >= 10
|
|
153
|
+
# end
|
|
154
|
+
def poll_nb_each
|
|
155
|
+
closed_consumer_check(__method__)
|
|
156
|
+
|
|
157
|
+
@native_kafka.with_inner do |inner|
|
|
158
|
+
loop do
|
|
159
|
+
message_ptr = Rdkafka::Bindings.rd_kafka_consumer_poll_nb(inner, 0)
|
|
160
|
+
break if message_ptr.null?
|
|
161
|
+
|
|
162
|
+
begin
|
|
163
|
+
native_message = Rdkafka::Bindings::Message.new(message_ptr)
|
|
164
|
+
|
|
165
|
+
if native_message[:err] != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
166
|
+
raise Rdkafka::RdkafkaError.new(native_message[:err])
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
result = yield Consumer::Message.new(native_message)
|
|
170
|
+
break if result == :stop
|
|
171
|
+
ensure
|
|
172
|
+
Rdkafka::Bindings.rd_kafka_message_destroy(message_ptr)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
41
176
|
end
|
|
42
177
|
|
|
43
178
|
# Close this consumer
|
|
@@ -78,7 +213,7 @@ module Rdkafka
|
|
|
78
213
|
Rdkafka::Bindings.rd_kafka_subscribe(inner, tpl)
|
|
79
214
|
end
|
|
80
215
|
if response != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
81
|
-
raise Rdkafka::RdkafkaError.new(response, "Error subscribing to '#{topics.join(
|
|
216
|
+
raise Rdkafka::RdkafkaError.new(response, "Error subscribing to '#{topics.join(", ")}'")
|
|
82
217
|
end
|
|
83
218
|
ensure
|
|
84
219
|
Rdkafka::Bindings.rd_kafka_topic_partition_list_destroy(tpl) unless tpl.nil?
|
|
@@ -228,7 +363,7 @@ module Rdkafka
|
|
|
228
363
|
end
|
|
229
364
|
end
|
|
230
365
|
ensure
|
|
231
|
-
ptr
|
|
366
|
+
ptr&.free
|
|
232
367
|
end
|
|
233
368
|
|
|
234
369
|
# @return [Boolean] true if our current assignment has been lost involuntarily.
|
|
@@ -281,7 +416,7 @@ module Rdkafka
|
|
|
281
416
|
# @return [TopicPartitionList]
|
|
282
417
|
#
|
|
283
418
|
# @raise [RdkafkaError] When getting the positions fails.
|
|
284
|
-
def position(list=nil)
|
|
419
|
+
def position(list = nil)
|
|
285
420
|
if list.nil?
|
|
286
421
|
list = assignment
|
|
287
422
|
elsif !list.is_a?(TopicPartitionList)
|
|
@@ -321,17 +456,17 @@ module Rdkafka
|
|
|
321
456
|
partition,
|
|
322
457
|
low,
|
|
323
458
|
high,
|
|
324
|
-
timeout_ms
|
|
459
|
+
timeout_ms
|
|
325
460
|
)
|
|
326
461
|
end
|
|
327
462
|
if response != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
328
463
|
raise Rdkafka::RdkafkaError.new(response, "Error querying watermark offsets for partition #{partition} of #{topic}")
|
|
329
464
|
end
|
|
330
465
|
|
|
331
|
-
|
|
466
|
+
[low.read_array_of_int64(1).first, high.read_array_of_int64(1).first]
|
|
332
467
|
ensure
|
|
333
|
-
low
|
|
334
|
-
high
|
|
468
|
+
low&.free
|
|
469
|
+
high&.free
|
|
335
470
|
end
|
|
336
471
|
|
|
337
472
|
# Calculate the consumer lag per partition for the provided topic partition list.
|
|
@@ -510,14 +645,14 @@ module Rdkafka
|
|
|
510
645
|
# @param async [Boolean] Whether to commit async or wait for the commit to finish
|
|
511
646
|
# @return [nil]
|
|
512
647
|
# @raise [RdkafkaError] When committing fails
|
|
513
|
-
def commit(list=nil, async=false)
|
|
648
|
+
def commit(list = nil, async = false)
|
|
514
649
|
closed_consumer_check(__method__)
|
|
515
650
|
|
|
516
651
|
if !list.nil? && !list.is_a?(TopicPartitionList)
|
|
517
652
|
raise TypeError.new("list has to be nil or a TopicPartitionList")
|
|
518
653
|
end
|
|
519
654
|
|
|
520
|
-
tpl = list
|
|
655
|
+
tpl = list&.to_native_tpl
|
|
521
656
|
|
|
522
657
|
begin
|
|
523
658
|
response = @native_kafka.with_inner do |inner|
|
|
@@ -561,6 +696,47 @@ module Rdkafka
|
|
|
561
696
|
end
|
|
562
697
|
end
|
|
563
698
|
|
|
699
|
+
# Poll for the next message without releasing the GVL (Global VM Lock).
|
|
700
|
+
#
|
|
701
|
+
# This is more efficient than regular polling for non-blocking poll(0) calls,
|
|
702
|
+
# particularly useful in fiber scheduler contexts where GVL release/reacquire
|
|
703
|
+
# overhead is wasteful since we don't expect to wait.
|
|
704
|
+
#
|
|
705
|
+
# @param timeout_ms [Integer] Timeout of this poll (default: 0 for non-blocking)
|
|
706
|
+
# @return [Message, nil] A message or nil if there was no new message within the timeout
|
|
707
|
+
# @raise [RdkafkaError] When polling fails
|
|
708
|
+
#
|
|
709
|
+
# @example Using with fiber scheduler
|
|
710
|
+
# # After receiving IO notification that messages are available
|
|
711
|
+
# while msg = consumer.poll_nb
|
|
712
|
+
# process(msg)
|
|
713
|
+
# end
|
|
714
|
+
def poll_nb(timeout_ms = 0)
|
|
715
|
+
closed_consumer_check(__method__)
|
|
716
|
+
|
|
717
|
+
message_ptr = @native_kafka.with_inner do |inner|
|
|
718
|
+
Rdkafka::Bindings.rd_kafka_consumer_poll_nb(inner, timeout_ms)
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
if message_ptr.null?
|
|
722
|
+
nil
|
|
723
|
+
else
|
|
724
|
+
# Create struct wrapper
|
|
725
|
+
native_message = Rdkafka::Bindings::Message.new(message_ptr)
|
|
726
|
+
# Raise error if needed
|
|
727
|
+
if native_message[:err] != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
728
|
+
raise Rdkafka::RdkafkaError.new(native_message[:err])
|
|
729
|
+
end
|
|
730
|
+
# Create a message to pass out
|
|
731
|
+
Rdkafka::Consumer::Message.new(native_message)
|
|
732
|
+
end
|
|
733
|
+
ensure
|
|
734
|
+
# Clean up rdkafka message if there is one
|
|
735
|
+
if message_ptr && !message_ptr.null?
|
|
736
|
+
Rdkafka::Bindings.rd_kafka_message_destroy(message_ptr)
|
|
737
|
+
end
|
|
738
|
+
end
|
|
739
|
+
|
|
564
740
|
# Polls the main rdkafka queue (not the consumer one). Do **NOT** use it if `consumer_poll_set`
|
|
565
741
|
# was set to `true`.
|
|
566
742
|
#
|
|
@@ -587,6 +763,22 @@ module Rdkafka
|
|
|
587
763
|
end
|
|
588
764
|
end
|
|
589
765
|
|
|
766
|
+
# Polls the main rdkafka queue without releasing the GVL (Global VM Lock).
|
|
767
|
+
#
|
|
768
|
+
# This is more efficient than regular events_poll for non-blocking poll(0) calls,
|
|
769
|
+
# particularly useful in fiber scheduler contexts where GVL release/reacquire
|
|
770
|
+
# overhead is wasteful since we don't expect to wait.
|
|
771
|
+
#
|
|
772
|
+
# @param timeout_ms [Integer] poll timeout (default: 0 for non-blocking)
|
|
773
|
+
# @return [Integer] the number of events served
|
|
774
|
+
#
|
|
775
|
+
# @see #events_poll for more details on when to use this method
|
|
776
|
+
def events_poll_nb(timeout_ms = 0)
|
|
777
|
+
@native_kafka.with_inner do |inner|
|
|
778
|
+
Rdkafka::Bindings.rd_kafka_poll_nb(inner, timeout_ms)
|
|
779
|
+
end
|
|
780
|
+
end
|
|
781
|
+
|
|
590
782
|
# Poll for new messages and yield for each received one. Iteration
|
|
591
783
|
# will end when the consumer is closed.
|
|
592
784
|
#
|
|
@@ -602,12 +794,10 @@ module Rdkafka
|
|
|
602
794
|
message = poll(timeout_ms)
|
|
603
795
|
if message
|
|
604
796
|
yield(message)
|
|
797
|
+
elsif closed?
|
|
798
|
+
break
|
|
605
799
|
else
|
|
606
|
-
|
|
607
|
-
break
|
|
608
|
-
else
|
|
609
|
-
next
|
|
610
|
-
end
|
|
800
|
+
next
|
|
611
801
|
end
|
|
612
802
|
end
|
|
613
803
|
end
|
data/lib/rdkafka/error.rb
CHANGED
|
@@ -18,15 +18,21 @@ module Rdkafka
|
|
|
18
18
|
# @return [String]
|
|
19
19
|
attr_reader :broker_message
|
|
20
20
|
|
|
21
|
+
# The name of the rdkafka instance that generated this error
|
|
22
|
+
# @return [String, nil]
|
|
23
|
+
attr_reader :instance_name
|
|
24
|
+
|
|
21
25
|
# @private
|
|
22
26
|
# @param response [Integer] the raw error response code from librdkafka
|
|
23
27
|
# @param message_prefix [String, nil] optional prefix for error messages
|
|
24
28
|
# @param broker_message [String, nil] optional error message from the broker
|
|
25
|
-
|
|
29
|
+
# @param instance_name [String, nil] optional name of the rdkafka instance
|
|
30
|
+
def initialize(response, message_prefix = nil, broker_message: nil, instance_name: nil)
|
|
26
31
|
raise TypeError.new("Response has to be an integer") unless response.is_a? Integer
|
|
27
32
|
@rdkafka_response = response
|
|
28
33
|
@message_prefix = message_prefix
|
|
29
34
|
@broker_message = broker_message
|
|
35
|
+
@instance_name = instance_name
|
|
30
36
|
end
|
|
31
37
|
|
|
32
38
|
# This error's code, for example `:partition_eof`, `:msg_size_too_large`.
|
|
@@ -34,7 +40,7 @@ module Rdkafka
|
|
|
34
40
|
def code
|
|
35
41
|
code = Rdkafka::Bindings.rd_kafka_err2name(@rdkafka_response).downcase
|
|
36
42
|
if code[0] == "_"
|
|
37
|
-
code[1
|
|
43
|
+
code[1..].to_sym
|
|
38
44
|
else
|
|
39
45
|
code.to_sym
|
|
40
46
|
end
|
|
@@ -44,11 +50,16 @@ module Rdkafka
|
|
|
44
50
|
# @return [String]
|
|
45
51
|
def to_s
|
|
46
52
|
message_prefix_part = if message_prefix
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
"#{message_prefix} - "
|
|
54
|
+
else
|
|
55
|
+
""
|
|
56
|
+
end
|
|
57
|
+
instance_name_part = if instance_name
|
|
58
|
+
" [#{instance_name}]"
|
|
59
|
+
else
|
|
60
|
+
""
|
|
61
|
+
end
|
|
62
|
+
"#{message_prefix_part}#{Rdkafka::Bindings.rd_kafka_err2str(@rdkafka_response)} (#{code})#{instance_name_part}"
|
|
52
63
|
end
|
|
53
64
|
|
|
54
65
|
# Whether this error indicates the partition is EOF.
|
|
@@ -58,10 +69,10 @@ module Rdkafka
|
|
|
58
69
|
end
|
|
59
70
|
|
|
60
71
|
# Error comparison
|
|
61
|
-
# @param
|
|
72
|
+
# @param other [Object] object to compare with
|
|
62
73
|
# @return [Boolean]
|
|
63
|
-
def ==(
|
|
64
|
-
|
|
74
|
+
def ==(other)
|
|
75
|
+
other.is_a?(self.class) && (to_s == other.to_s)
|
|
65
76
|
end
|
|
66
77
|
end
|
|
67
78
|
|
|
@@ -74,7 +85,7 @@ module Rdkafka
|
|
|
74
85
|
# @param response [Integer] the raw error response code from librdkafka
|
|
75
86
|
# @param topic_partition_list [TopicPartitionList] the topic partition list with error info
|
|
76
87
|
# @param message_prefix [String, nil] optional prefix for error messages
|
|
77
|
-
def initialize(response, topic_partition_list, message_prefix=nil)
|
|
88
|
+
def initialize(response, topic_partition_list, message_prefix = nil)
|
|
78
89
|
super(response, message_prefix)
|
|
79
90
|
@topic_partition_list = topic_partition_list
|
|
80
91
|
end
|
|
@@ -84,7 +95,7 @@ module Rdkafka
|
|
|
84
95
|
class ClosedConsumerError < BaseError
|
|
85
96
|
# @param method [Symbol] the method that was called
|
|
86
97
|
def initialize(method)
|
|
87
|
-
super("Illegal call to #{method
|
|
98
|
+
super("Illegal call to #{method} on a closed consumer")
|
|
88
99
|
end
|
|
89
100
|
end
|
|
90
101
|
|
|
@@ -92,7 +103,7 @@ module Rdkafka
|
|
|
92
103
|
class ClosedProducerError < BaseError
|
|
93
104
|
# @param method [Symbol] the method that was called
|
|
94
105
|
def initialize(method)
|
|
95
|
-
super("Illegal call to #{method
|
|
106
|
+
super("Illegal call to #{method} on a closed producer")
|
|
96
107
|
end
|
|
97
108
|
end
|
|
98
109
|
|
|
@@ -100,7 +111,7 @@ module Rdkafka
|
|
|
100
111
|
class ClosedAdminError < BaseError
|
|
101
112
|
# @param method [Symbol] the method that was called
|
|
102
113
|
def initialize(method)
|
|
103
|
-
super("Illegal call to #{method
|
|
114
|
+
super("Illegal call to #{method} on a closed admin")
|
|
104
115
|
end
|
|
105
116
|
end
|
|
106
117
|
|
|
@@ -2,7 +2,6 @@ module Rdkafka
|
|
|
2
2
|
module Helpers
|
|
3
3
|
# OAuth helper methods for setting and refreshing SASL/OAUTHBEARER tokens
|
|
4
4
|
module OAuth
|
|
5
|
-
|
|
6
5
|
# Set the OAuthBearer token
|
|
7
6
|
#
|
|
8
7
|
# @param token [String] the mandatory token value to set, often (but not necessarily) a JWS compact serialization as per https://tools.ietf.org/html/rfc7515#section-3.1.
|
data/lib/rdkafka/helpers/time.rb
CHANGED
|
@@ -9,6 +9,11 @@ module Rdkafka
|
|
|
9
9
|
def monotonic_now
|
|
10
10
|
::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
11
11
|
end
|
|
12
|
+
|
|
13
|
+
# @return [Integer] current monotonic time in milliseconds
|
|
14
|
+
def monotonic_now_ms
|
|
15
|
+
::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond)
|
|
16
|
+
end
|
|
12
17
|
end
|
|
13
18
|
end
|
|
14
19
|
end
|
data/lib/rdkafka/metadata.rb
CHANGED
|
@@ -100,40 +100,40 @@ module Rdkafka
|
|
|
100
100
|
# FFI struct for rd_kafka_metadata_t
|
|
101
101
|
class Metadata < CustomFFIStruct
|
|
102
102
|
layout :brokers_count, :int,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
:brokers_metadata, :pointer,
|
|
104
|
+
:topics_count, :int,
|
|
105
|
+
:topics_metadata, :pointer,
|
|
106
|
+
:broker_id, :int32,
|
|
107
|
+
:broker_name, :string
|
|
108
108
|
end
|
|
109
109
|
|
|
110
110
|
# @private
|
|
111
111
|
# FFI struct for rd_kafka_metadata_broker_t
|
|
112
112
|
class BrokerMetadata < CustomFFIStruct
|
|
113
113
|
layout :broker_id, :int32,
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
:broker_name, :string,
|
|
115
|
+
:broker_port, :int
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
# @private
|
|
119
119
|
# FFI struct for rd_kafka_metadata_topic_t
|
|
120
120
|
class TopicMetadata < CustomFFIStruct
|
|
121
121
|
layout :topic_name, :string,
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
:partition_count, :int,
|
|
123
|
+
:partitions_metadata, :pointer,
|
|
124
|
+
:rd_kafka_resp_err, :int
|
|
125
125
|
end
|
|
126
126
|
|
|
127
127
|
# @private
|
|
128
128
|
# FFI struct for rd_kafka_metadata_partition_t
|
|
129
129
|
class PartitionMetadata < CustomFFIStruct
|
|
130
130
|
layout :partition_id, :int32,
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
131
|
+
:rd_kafka_resp_err, :int,
|
|
132
|
+
:leader, :int32,
|
|
133
|
+
:replica_count, :int,
|
|
134
|
+
:replicas, :pointer,
|
|
135
|
+
:in_sync_replica_brokers, :int,
|
|
136
|
+
:isrs, :pointer
|
|
137
137
|
end
|
|
138
138
|
end
|
|
139
139
|
end
|
data/lib/rdkafka/native_kafka.rb
CHANGED
|
@@ -70,7 +70,7 @@ module Rdkafka
|
|
|
70
70
|
end
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
@polling_thread.name = "rdkafka.native_kafka##{Rdkafka::Bindings.rd_kafka_name(@inner).gsub(
|
|
73
|
+
@polling_thread.name = "rdkafka.native_kafka##{Rdkafka::Bindings.rd_kafka_name(@inner).gsub("rdkafka", "")}"
|
|
74
74
|
@polling_thread.abort_on_exception = true
|
|
75
75
|
@polling_thread[:closing] = false
|
|
76
76
|
end
|
|
@@ -121,11 +121,72 @@ module Rdkafka
|
|
|
121
121
|
@closing || @inner.nil?
|
|
122
122
|
end
|
|
123
123
|
|
|
124
|
+
# Enable IO event notifications on the main queue
|
|
125
|
+
# Librdkafka will write to your FD when the queue transitions from empty to non-empty
|
|
126
|
+
#
|
|
127
|
+
# @note This method is incompatible with background polling threads.
|
|
128
|
+
# If background polling is enabled, use manual polling instead (e.g., consumer.poll)
|
|
129
|
+
#
|
|
130
|
+
# @param fd [Integer] your file descriptor (from IO.pipe or eventfd)
|
|
131
|
+
# @param payload [String] data to write to fd when queue has data (default: "\x01")
|
|
132
|
+
# @return [nil]
|
|
133
|
+
# @raise [ClosedInnerError] when the handle is closed
|
|
134
|
+
# @raise [RuntimeError] when background polling thread is active
|
|
135
|
+
#
|
|
136
|
+
# @example
|
|
137
|
+
# # Create your own signaling FD
|
|
138
|
+
# signal_r, signal_w = IO.pipe
|
|
139
|
+
# native_kafka.enable_main_queue_io_events(signal_w.fileno)
|
|
140
|
+
#
|
|
141
|
+
# # Monitor it with select
|
|
142
|
+
# readable, = IO.select([signal_r], nil, nil, timeout)
|
|
143
|
+
# if readable
|
|
144
|
+
# consumer.poll(0) # Get messages
|
|
145
|
+
# end
|
|
146
|
+
def enable_main_queue_io_events(fd, payload = "\x01")
|
|
147
|
+
if @run_polling_thread
|
|
148
|
+
raise "Cannot enable IO events while background polling thread is active. " \
|
|
149
|
+
"Either disable background polling by setting run_polling_thread: false, " \
|
|
150
|
+
"or use manual polling with consumer.poll() instead of the FD API."
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
with_inner do |inner|
|
|
154
|
+
queue_ptr = Bindings.rd_kafka_queue_get_main(inner)
|
|
155
|
+
Bindings.rd_kafka_queue_io_event_enable(queue_ptr, fd, payload, payload.bytesize)
|
|
156
|
+
Bindings.rd_kafka_queue_destroy(queue_ptr)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Enable IO event notifications on the background queue
|
|
161
|
+
# Librdkafka will write to your FD when the background queue transitions from empty to non-empty
|
|
162
|
+
#
|
|
163
|
+
# @note This method is incompatible with background polling threads.
|
|
164
|
+
# If background polling is enabled, use manual polling instead (e.g., consumer.poll)
|
|
165
|
+
#
|
|
166
|
+
# @param fd [Integer] your file descriptor (from IO.pipe or eventfd)
|
|
167
|
+
# @param payload [String] data to write to fd when queue has data (default: "\x01")
|
|
168
|
+
# @return [nil]
|
|
169
|
+
# @raise [ClosedInnerError] when the handle is closed
|
|
170
|
+
# @raise [RuntimeError] when background polling thread is active
|
|
171
|
+
def enable_background_queue_io_events(fd, payload = "\x01")
|
|
172
|
+
if @run_polling_thread
|
|
173
|
+
raise "Cannot enable IO events while background polling thread is active. " \
|
|
174
|
+
"Either disable background polling by setting run_polling_thread: false, " \
|
|
175
|
+
"or use manual polling with consumer.poll() instead of the FD API."
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
with_inner do |inner|
|
|
179
|
+
queue_ptr = Bindings.rd_kafka_queue_get_background(inner)
|
|
180
|
+
Bindings.rd_kafka_queue_io_event_enable(queue_ptr, fd, payload, payload.bytesize)
|
|
181
|
+
Bindings.rd_kafka_queue_destroy(queue_ptr)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
124
185
|
# Closes the native Kafka handle and cleans up resources
|
|
125
186
|
# @param object_id [Integer, nil] optional object ID (unused, for finalizer compatibility)
|
|
126
187
|
# @yield optional block to execute before destroying the handle
|
|
127
188
|
# @return [nil]
|
|
128
|
-
def close(object_id=nil)
|
|
189
|
+
def close(object_id = nil)
|
|
129
190
|
return if closed?
|
|
130
191
|
|
|
131
192
|
synchronize do
|