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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d5a0a12c81986370bd3e74f1d50e072a1efaec78f87b9920ffe0c7a9633192c5
4
- data.tar.gz: 3c1ff95662b2ba5bb6912ebeda520209f7c6b818762b3fb5f243a4751f0b2b10
3
+ metadata.gz: 67aea1d29ab7c9accce635b701dc80e09e2a07ee8208554c5a8be054aa85319d
4
+ data.tar.gz: ea2677a630149511b68987a5dd18f7908def22b7325540ae8e5b238d32e94ca4
5
5
  SHA512:
6
- metadata.gz: c72c872626ef1d51b63f585a65a472d7d7b94fb050a610be91f409b686c010f64167d9bca633edbf2862ebb40c413156f9ae422045e103e2537e85e8bbb2cafd
7
- data.tar.gz: f45068691155efba1f50b5f7b0794f2d23a2ae4e7f8db9651af2f5bed98e18c053a8c79e21ccbf19d6dfa434689740718e4e9e3b187b84a0d269fe17e822411a
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
@@ -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
@@ -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
  #
@@ -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] = [monotonic_now, new_count]
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] = monotonic_now
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] = monotonic_now
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] = monotonic_now
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 [Float] Monotonic timestamp to check
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
- (monotonic_now - timestamp) * 1_000 > @ttl_ms
222
+ monotonic_now_ms - timestamp > @ttl_ms
223
223
  end
224
224
  end
225
225
  end
@@ -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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Rdkafka
4
4
  # Current rdkafka-ruby gem version
5
- VERSION = "0.24.0.rc2"
5
+ VERSION = "0.24.0.rc4"
6
6
  # Target librdkafka version to be used
7
7
  LIBRDKAFKA_VERSION = "2.13.0"
8
8
  # SHA256 hash of the librdkafka source tarball for verification
data/renovate.json CHANGED
@@ -5,6 +5,7 @@
5
5
  ],
6
6
  "minimumReleaseAge": "7 days",
7
7
  "includePaths": [
8
+ ".ruby-version",
8
9
  "Gemfile",
9
10
  "Gemfile.lint",
10
11
  "package.json",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: karafka-rdkafka
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.24.0.rc2
4
+ version: 0.24.0.rc4
5
5
  platform: aarch64-linux-gnu
6
6
  authors:
7
7
  - Thijs Cadier