karafka-rdkafka 0.24.0.rc2-aarch64-linux-gnu → 0.24.0.rc4-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 +3 -0
- data/ext/librdkafka.so +0 -0
- data/lib/rdkafka/admin.rb +39 -0
- data/lib/rdkafka/bindings.rb +9 -0
- data/lib/rdkafka/consumer.rb +154 -0
- data/lib/rdkafka/helpers/time.rb +5 -0
- data/lib/rdkafka/producer/partitions_count_cache.rb +6 -6
- data/lib/rdkafka/producer.rb +39 -0
- data/lib/rdkafka/version.rb +1 -1
- data/renovate.json +1 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 67aea1d29ab7c9accce635b701dc80e09e2a07ee8208554c5a8be054aa85319d
|
|
4
|
+
data.tar.gz: ea2677a630149511b68987a5dd18f7908def22b7325540ae8e5b238d32e94ca4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 72e001033ae77d321f97b1aa4d9bc15fa01bf99c1553b36f477f7d0c189fd8d89dd02866f9ce8989be1e3f7a4dca554af84543e5cbdc8d69cd2992220814defd
|
|
7
|
+
data.tar.gz: 690df2547a6bdafdd3716490181c78e6a1ff2b61f2008a62e0c9aa397560b7839d5740153ac850d23213e6c6d264ddbf75f1a6c24d8dfc059e1d79f6e6529a8b
|
data/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
- [Enhancement] Use native ARM64 runners instead of QEMU emulation for Alpine musl aarch64 builds, improving build performance and reliability (from upstream).
|
|
16
16
|
- [Enhancement] Enable parallel compilation (`make -j$(nproc)`) for ARM64 Alpine musl builds (from upstream).
|
|
17
17
|
- [Enhancement] Bump librdkafka to 2.13.0.
|
|
18
|
+
- [Enhancement] Add non-blocking poll methods (`poll_nb`, `events_poll_nb`) that skip GVL release for efficient fiber scheduler integration when using `poll(0)` (from upstream).
|
|
19
|
+
- [Enhancement] Add `events_poll_nb_each` method on `Producer`, `Consumer`, and `Admin` for polling events in a single GVL/mutex session. Yields count after each iteration, caller returns `:stop` to break (from upstream).
|
|
20
|
+
- [Enhancement] Add `poll_nb_each` method on `Consumer` for non-blocking message polling with proper resource cleanup, yielding each message and supporting early termination via `:stop` return value (from upstream).
|
|
18
21
|
- [Fix] Fix Kerberos build on Alpine 3.23+ (GCC 15/C23) by forcing C17 semantics to maintain compatibility with old-style K&R declarations in MIT Kerberos and Cyrus SASL dependencies.
|
|
19
22
|
|
|
20
23
|
## 0.23.1 (2025-11-14)
|
data/ext/librdkafka.so
CHANGED
|
Binary file
|
data/lib/rdkafka/admin.rb
CHANGED
|
@@ -91,6 +91,45 @@ module Rdkafka
|
|
|
91
91
|
@native_kafka.enable_background_queue_io_events(fd, payload)
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
+
# Polls for events in a non-blocking loop, yielding the count after each iteration.
|
|
95
|
+
#
|
|
96
|
+
# This method processes events (stats, errors, etc.) in a single GVL/mutex session,
|
|
97
|
+
# which is more efficient than repeated individual polls. It uses non-blocking polls
|
|
98
|
+
# internally (no GVL release between polls).
|
|
99
|
+
#
|
|
100
|
+
# Yields the count of events processed after each poll iteration, allowing the caller
|
|
101
|
+
# to implement timeout or other termination logic by returning `:stop`.
|
|
102
|
+
#
|
|
103
|
+
# @yield [count] Called after each poll iteration
|
|
104
|
+
# @yieldparam count [Integer] Number of events processed in this iteration
|
|
105
|
+
# @yieldreturn [Symbol, Object] Return `:stop` to break the loop, any other value continues
|
|
106
|
+
# @return [nil]
|
|
107
|
+
# @raise [Rdkafka::ClosedAdminError] if called on a closed admin client
|
|
108
|
+
#
|
|
109
|
+
# @note This method holds the inner lock until the queue is empty or `:stop` is returned.
|
|
110
|
+
# Other admin operations will wait until this method returns.
|
|
111
|
+
# @note This method is thread-safe as it uses @native_kafka.with_inner synchronization
|
|
112
|
+
#
|
|
113
|
+
# @example Drain all pending events
|
|
114
|
+
# admin.events_poll_nb_each { |_count| }
|
|
115
|
+
#
|
|
116
|
+
# @example With timeout control
|
|
117
|
+
# deadline = monotonic_now + timeout_ms
|
|
118
|
+
# admin.events_poll_nb_each do |_count|
|
|
119
|
+
# :stop if monotonic_now >= deadline
|
|
120
|
+
# end
|
|
121
|
+
def events_poll_nb_each
|
|
122
|
+
closed_admin_check(__method__)
|
|
123
|
+
|
|
124
|
+
@native_kafka.with_inner do |inner|
|
|
125
|
+
loop do
|
|
126
|
+
count = Rdkafka::Bindings.rd_kafka_poll_nb(inner, 0)
|
|
127
|
+
break if count.zero?
|
|
128
|
+
break if yield(count) == :stop
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
94
133
|
# @return [Proc] finalizer proc for closing the admin
|
|
95
134
|
# @private
|
|
96
135
|
def finalizer
|
data/lib/rdkafka/bindings.rb
CHANGED
|
@@ -96,6 +96,12 @@ module Rdkafka
|
|
|
96
96
|
attach_function :rd_kafka_poll, [:pointer, :int], :int, blocking: true
|
|
97
97
|
attach_function :rd_kafka_outq_len, [:pointer], :int, blocking: true
|
|
98
98
|
|
|
99
|
+
# Non-blocking poll variants (do not release GVL)
|
|
100
|
+
# These are more efficient for poll(0) calls in fiber schedulers where GVL
|
|
101
|
+
# release/reacquire overhead is wasteful since we don't expect to wait.
|
|
102
|
+
# Uses the same underlying C function but with blocking: false to skip GVL release.
|
|
103
|
+
attach_function :rd_kafka_poll_nb, :rd_kafka_poll, [:pointer, :int], :int, blocking: false
|
|
104
|
+
|
|
99
105
|
# Metadata
|
|
100
106
|
|
|
101
107
|
attach_function :rd_kafka_name, [:pointer], :string
|
|
@@ -398,6 +404,9 @@ module Rdkafka
|
|
|
398
404
|
attach_function :rd_kafka_commit, [:pointer, :pointer, :bool], :int, blocking: true
|
|
399
405
|
attach_function :rd_kafka_poll_set_consumer, [:pointer], :void, blocking: true
|
|
400
406
|
attach_function :rd_kafka_consumer_poll, [:pointer, :int], :pointer, blocking: true
|
|
407
|
+
# Non-blocking consumer poll variant (does not release GVL)
|
|
408
|
+
# More efficient for poll(0) calls in fiber schedulers.
|
|
409
|
+
attach_function :rd_kafka_consumer_poll_nb, :rd_kafka_consumer_poll, [:pointer, :int], :pointer, blocking: false
|
|
401
410
|
attach_function :rd_kafka_consumer_close, [:pointer], :void, blocking: true
|
|
402
411
|
attach_function :rd_kafka_offsets_store, [:pointer, :pointer], :int, blocking: true
|
|
403
412
|
attach_function :rd_kafka_pause_partitions, [:pointer, :pointer], :int, blocking: true
|
data/lib/rdkafka/consumer.rb
CHANGED
|
@@ -75,6 +75,103 @@ module Rdkafka
|
|
|
75
75
|
@native_kafka.enable_background_queue_io_events(fd, payload)
|
|
76
76
|
end
|
|
77
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
|
+
|
|
78
175
|
# @return [Proc] finalizer proc for closing the consumer
|
|
79
176
|
# @private
|
|
80
177
|
def finalizer
|
|
@@ -606,6 +703,47 @@ module Rdkafka
|
|
|
606
703
|
end
|
|
607
704
|
end
|
|
608
705
|
|
|
706
|
+
# Poll for the next message without releasing the GVL (Global VM Lock).
|
|
707
|
+
#
|
|
708
|
+
# This is more efficient than regular polling for non-blocking poll(0) calls,
|
|
709
|
+
# particularly useful in fiber scheduler contexts where GVL release/reacquire
|
|
710
|
+
# overhead is wasteful since we don't expect to wait.
|
|
711
|
+
#
|
|
712
|
+
# @param timeout_ms [Integer] Timeout of this poll (default: 0 for non-blocking)
|
|
713
|
+
# @return [Message, nil] A message or nil if there was no new message within the timeout
|
|
714
|
+
# @raise [RdkafkaError] When polling fails
|
|
715
|
+
#
|
|
716
|
+
# @example Using with fiber scheduler
|
|
717
|
+
# # After receiving IO notification that messages are available
|
|
718
|
+
# while msg = consumer.poll_nb
|
|
719
|
+
# process(msg)
|
|
720
|
+
# end
|
|
721
|
+
def poll_nb(timeout_ms = 0)
|
|
722
|
+
closed_consumer_check(__method__)
|
|
723
|
+
|
|
724
|
+
message_ptr = @native_kafka.with_inner do |inner|
|
|
725
|
+
Rdkafka::Bindings.rd_kafka_consumer_poll_nb(inner, timeout_ms)
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
if message_ptr.null?
|
|
729
|
+
nil
|
|
730
|
+
else
|
|
731
|
+
# Create struct wrapper
|
|
732
|
+
native_message = Rdkafka::Bindings::Message.new(message_ptr)
|
|
733
|
+
# Raise error if needed
|
|
734
|
+
if native_message[:err] != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
735
|
+
raise Rdkafka::RdkafkaError.new(native_message[:err])
|
|
736
|
+
end
|
|
737
|
+
# Create a message to pass out
|
|
738
|
+
Rdkafka::Consumer::Message.new(native_message)
|
|
739
|
+
end
|
|
740
|
+
ensure
|
|
741
|
+
# Clean up rdkafka message if there is one
|
|
742
|
+
if message_ptr && !message_ptr.null?
|
|
743
|
+
Rdkafka::Bindings.rd_kafka_message_destroy(message_ptr)
|
|
744
|
+
end
|
|
745
|
+
end
|
|
746
|
+
|
|
609
747
|
# Polls the main rdkafka queue (not the consumer one). Do **NOT** use it if `consumer_poll_set`
|
|
610
748
|
# was set to `true`.
|
|
611
749
|
#
|
|
@@ -632,6 +770,22 @@ module Rdkafka
|
|
|
632
770
|
end
|
|
633
771
|
end
|
|
634
772
|
|
|
773
|
+
# Polls the main rdkafka queue without releasing the GVL (Global VM Lock).
|
|
774
|
+
#
|
|
775
|
+
# This is more efficient than regular events_poll for non-blocking poll(0) calls,
|
|
776
|
+
# particularly useful in fiber scheduler contexts where GVL release/reacquire
|
|
777
|
+
# overhead is wasteful since we don't expect to wait.
|
|
778
|
+
#
|
|
779
|
+
# @param timeout_ms [Integer] poll timeout (default: 0 for non-blocking)
|
|
780
|
+
# @return [Integer] the number of events served
|
|
781
|
+
#
|
|
782
|
+
# @see #events_poll for more details on when to use this method
|
|
783
|
+
def events_poll_nb(timeout_ms = 0)
|
|
784
|
+
@native_kafka.with_inner do |inner|
|
|
785
|
+
Rdkafka::Bindings.rd_kafka_poll_nb(inner, timeout_ms)
|
|
786
|
+
end
|
|
787
|
+
end
|
|
788
|
+
|
|
635
789
|
# Poll for new messages and yield for each received one. Iteration
|
|
636
790
|
# will end when the consumer is closed.
|
|
637
791
|
#
|
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
|
|
@@ -147,17 +147,17 @@ module Rdkafka
|
|
|
147
147
|
|
|
148
148
|
if current_info.nil?
|
|
149
149
|
# Create new entry
|
|
150
|
-
@counts[topic] = [
|
|
150
|
+
@counts[topic] = [monotonic_now_ms, new_count]
|
|
151
151
|
else
|
|
152
152
|
current_count = current_info[1]
|
|
153
153
|
|
|
154
154
|
if new_count > current_count
|
|
155
155
|
# Update to higher count value
|
|
156
|
-
current_info[0] =
|
|
156
|
+
current_info[0] = monotonic_now_ms
|
|
157
157
|
current_info[1] = new_count
|
|
158
158
|
else
|
|
159
159
|
# Same or lower count, update timestamp only
|
|
160
|
-
current_info[0] =
|
|
160
|
+
current_info[0] = monotonic_now_ms
|
|
161
161
|
end
|
|
162
162
|
end
|
|
163
163
|
end
|
|
@@ -211,15 +211,15 @@ module Rdkafka
|
|
|
211
211
|
return unless current_info
|
|
212
212
|
|
|
213
213
|
# Update the timestamp in-place
|
|
214
|
-
current_info[0] =
|
|
214
|
+
current_info[0] = monotonic_now_ms
|
|
215
215
|
end
|
|
216
216
|
|
|
217
217
|
# Check if a timestamp has expired based on the TTL
|
|
218
218
|
#
|
|
219
|
-
# @param timestamp [
|
|
219
|
+
# @param timestamp [Integer] Monotonic timestamp in milliseconds to check
|
|
220
220
|
# @return [Boolean] true if expired, false otherwise
|
|
221
221
|
def expired?(timestamp)
|
|
222
|
-
|
|
222
|
+
monotonic_now_ms - timestamp > @ttl_ms
|
|
223
223
|
end
|
|
224
224
|
end
|
|
225
225
|
end
|
data/lib/rdkafka/producer.rb
CHANGED
|
@@ -338,6 +338,45 @@ module Rdkafka
|
|
|
338
338
|
|
|
339
339
|
alias_method :queue_length, :queue_size
|
|
340
340
|
|
|
341
|
+
# Polls for events in a non-blocking loop, yielding the count after each iteration.
|
|
342
|
+
#
|
|
343
|
+
# This method processes delivery callbacks in a single GVL/mutex session, which is more
|
|
344
|
+
# efficient than repeated individual polls. It uses non-blocking polls internally
|
|
345
|
+
# (no GVL release between polls).
|
|
346
|
+
#
|
|
347
|
+
# Yields the count of events processed after each poll iteration, allowing the caller
|
|
348
|
+
# to implement timeout or other termination logic by returning `:stop`.
|
|
349
|
+
#
|
|
350
|
+
# @yield [count] Called after each poll iteration
|
|
351
|
+
# @yieldparam count [Integer] Number of events processed in this iteration
|
|
352
|
+
# @yieldreturn [Symbol, Object] Return `:stop` to break the loop, any other value continues
|
|
353
|
+
# @return [nil]
|
|
354
|
+
# @raise [Rdkafka::ClosedProducerError] if called on a closed producer
|
|
355
|
+
#
|
|
356
|
+
# @note This method holds the inner lock until the queue is empty or `:stop` is returned.
|
|
357
|
+
# Other producer operations (produce, close, etc.) will wait until this method returns.
|
|
358
|
+
# @note This method is thread-safe as it uses @native_kafka.with_inner synchronization
|
|
359
|
+
#
|
|
360
|
+
# @example Drain all pending callbacks
|
|
361
|
+
# producer.events_poll_nb_each { |_count| }
|
|
362
|
+
#
|
|
363
|
+
# @example With timeout control
|
|
364
|
+
# deadline = monotonic_now + timeout_ms
|
|
365
|
+
# producer.events_poll_nb_each do |_count|
|
|
366
|
+
# :stop if monotonic_now >= deadline
|
|
367
|
+
# end
|
|
368
|
+
def events_poll_nb_each
|
|
369
|
+
closed_producer_check(__method__)
|
|
370
|
+
|
|
371
|
+
@native_kafka.with_inner do |inner|
|
|
372
|
+
loop do
|
|
373
|
+
count = Rdkafka::Bindings.rd_kafka_poll_nb(inner, 0)
|
|
374
|
+
break if count.zero?
|
|
375
|
+
break if yield(count) == :stop
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
|
|
341
380
|
# Partition count for a given topic.
|
|
342
381
|
#
|
|
343
382
|
# @param topic [String] The topic name.
|
data/lib/rdkafka/version.rb
CHANGED
data/renovate.json
CHANGED