rdkafka 0.26.0 → 0.27.0

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: cfa1b93687c48e464d6ed74591aaa63a719f3f8278d24729d47ed4517942a4a2
4
- data.tar.gz: c9ae55f386c986a76bce3f1bb87b53647d8c657bb6a49a90eac556da32a272e8
3
+ metadata.gz: d818b1dce30cffe7b01f9961f05016b70bd99c823f487b3a2c075d335ab31dab
4
+ data.tar.gz: 5bfd08ae7392e5d13f7d06b4dc366515aacbd995d139b19a82dcc74b4324eaab
5
5
  SHA512:
6
- metadata.gz: 0d32f50f1313ff623dfde28d1453f7dadb0e8fd23049bfbfd8fdcd89ce4ce87611a496ad3a82ac57763827ba80cebf1b3801a7eb690a2b21abcde706e950c9a4
7
- data.tar.gz: 61f908671be7b0e72cd4670cb8c21ea7a644dbb69b36821f5896255da5c26f2dd6f18a75cfc30acfa05255c143e7826ea56f4d4ed27dcc8779cb99981917ff42
6
+ metadata.gz: b022eff9faf7d80689e17f832fc1017c395c3463100884fcd3c2fb3610a546cf5bf62362b6f2766ef3de13cf2c4ba1b0d6322dcd1908a38ab2db25a8530a3fda
7
+ data.tar.gz: f489a965325b6c7ab4faa6430b4fdc4912d9223e0cdf623df865e54f4c2baeae8e3d09f167f478d94b77b6f73ddad506b5b7d4f3098dc79c1a3dc58f85ebd941
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Rdkafka Changelog
2
2
 
3
+ ## 0.27.0 (2026-05-07)
4
+ - [Feature] Add `Consumer#poll_batch(timeout_ms, max_items:)` and `Consumer#poll_batch_nb(timeout_ms, max_items:)` for batch message polling via `rd_kafka_consume_batch_queue`.
5
+ - [Feature] Add `Config#describe_properties` to dump all librdkafka configuration properties (including defaults and hidden properties) as a Hash via `rd_kafka_conf_dump`.
6
+ - [Enhancement] Bump librdkafka to `2.14.0`
7
+ - [Fix] Fix resource leak in `Admin#describe_configs` and `Admin#incremental_alter_configs` where `admin_options_ptr` and `queue_ptr` were not destroyed in the ensure block.
8
+ - [Fix] Fix leaked queue reference in `Config#native_kafka` where `rd_kafka_queue_get_main` return value was not destroyed after passing to `rd_kafka_set_log_queue`.
9
+ - [Fix] Fix native topic partition list leak in `Consumer#position` where `tpl` was never destroyed.
10
+
3
11
  ## 0.26.0 (2026-04-02)
4
12
  - [Enhancement] Bump librdkafka to `2.13.2`
5
13
  - [Enhancement] Embed a per-file SPEC_HASH in test topic and consumer group names for tracing Kafka warnings back to specific spec files.
data/README.md CHANGED
@@ -163,7 +163,8 @@ bundle exec rake produce_messages
163
163
 
164
164
  | rdkafka-ruby | librdkafka | patches |
165
165
  |-|-|-|
166
- | 0.26.x (2026-03-30) | 2.13.2 (2026-03-30) | yes |
166
+ | 0.27.x (2026-05-07) | 2.14.0 (2026-04-01) | yes |
167
+ | 0.26.x (2026-03-30) | 2.13.2 (2026-03-02) | yes |
167
168
  | 0.25.x (2026-01-21) | 2.12.1 (2025-10-21) | yes |
168
169
  | 0.24.x (2025-10-10) | 2.11.1 (2025-08-18) | yes |
169
170
  | 0.23.x (2025-09-04) | 2.11.0 (2025-07-03) | yes |
data/lib/rdkafka/admin.rb CHANGED
@@ -777,6 +777,9 @@ module Rdkafka
777
777
 
778
778
  raise
779
779
  ensure
780
+ Rdkafka::Bindings.rd_kafka_AdminOptions_destroy(admin_options_ptr)
781
+ Rdkafka::Bindings.rd_kafka_queue_destroy(queue_ptr)
782
+
780
783
  if configs_array_ptr
781
784
  Rdkafka::Bindings.rd_kafka_ConfigResource_destroy_array(
782
785
  configs_array_ptr,
@@ -865,6 +868,9 @@ module Rdkafka
865
868
 
866
869
  raise
867
870
  ensure
871
+ Rdkafka::Bindings.rd_kafka_AdminOptions_destroy(admin_options_ptr)
872
+ Rdkafka::Bindings.rd_kafka_queue_destroy(queue_ptr)
873
+
868
874
  if configs_array_ptr
869
875
  Rdkafka::Bindings.rd_kafka_ConfigResource_destroy_array(
870
876
  configs_array_ptr,
@@ -234,6 +234,9 @@ module Rdkafka
234
234
 
235
235
  attach_function :rd_kafka_conf_new, [], :pointer
236
236
  attach_function :rd_kafka_conf_set, [:pointer, :string, :string, :pointer, :int], :kafka_config_response
237
+ attach_function :rd_kafka_conf_dump, [:pointer, :pointer], :pointer
238
+ attach_function :rd_kafka_conf_dump_free, [:pointer, :size_t], :void
239
+ attach_function :rd_kafka_conf_destroy, [:pointer], :void
237
240
  callback :log_cb, [:pointer, :int, :string, :string], :void
238
241
  attach_function :rd_kafka_conf_set_log_cb, [:pointer, :log_cb], :void
239
242
  attach_function :rd_kafka_conf_set_opaque, [:pointer, :pointer], :void
@@ -383,6 +386,9 @@ module Rdkafka
383
386
  # More efficient for poll(0) calls in fiber schedulers.
384
387
  attach_function :rd_kafka_consumer_poll_nb, :rd_kafka_consumer_poll, [:pointer, :int], :pointer, blocking: false
385
388
  attach_function :rd_kafka_consumer_close, [:pointer], :void, blocking: true
389
+ attach_function :rd_kafka_queue_get_consumer, [:pointer], :pointer
390
+ attach_function :rd_kafka_consume_batch_queue, [:pointer, :int, :pointer, :size_t], :ssize_t, blocking: true
391
+ attach_function :rd_kafka_consume_batch_queue_nb, :rd_kafka_consume_batch_queue, [:pointer, :int, :pointer, :size_t], :ssize_t, blocking: false
386
392
  attach_function :rd_kafka_offsets_store, [:pointer, :pointer], :int, blocking: true
387
393
  attach_function :rd_kafka_pause_partitions, [:pointer, :pointer], :int, blocking: true
388
394
  attach_function :rd_kafka_resume_partitions, [:pointer, :pointer], :int, blocking: true
@@ -285,6 +285,46 @@ module Rdkafka
285
285
  )
286
286
  end
287
287
 
288
+ # Returns all configuration properties and their current values for this config.
289
+ #
290
+ # Uses `rd_kafka_conf_dump` to retrieve every property (including defaults and
291
+ # internal properties like `client.software.name`) as a flat Hash.
292
+ #
293
+ # @note The librdkafka C API does not distinguish between producer-only, consumer-only,
294
+ # and global properties at the configuration level. All properties are returned
295
+ # regardless of the intended client type.
296
+ #
297
+ # @note The returned Hash may include sensitive values such as authentication
298
+ # credentials and key passwords. Do not log or serialize the returned data
299
+ # unless you have explicitly redacted secret entries.
300
+ #
301
+ # @return [Hash{Symbol => String}] property names mapped to their current values
302
+ #
303
+ # @raise [ConfigError] When the configuration contains invalid options
304
+ def describe_properties
305
+ config = nil
306
+ dump_ptr = nil
307
+ count = 0
308
+
309
+ config = native_config
310
+ count_ptr = Rdkafka::Bindings::SizePtr.new
311
+ dump_ptr = Rdkafka::Bindings.rd_kafka_conf_dump(config, count_ptr)
312
+
313
+ count = count_ptr[:value]
314
+ result = {}
315
+
316
+ (0...count).step(2) do |i|
317
+ key = dump_ptr.get_pointer(i * FFI::Pointer.size).read_string
318
+ value = dump_ptr.get_pointer((i + 1) * FFI::Pointer.size).read_string
319
+ result[key.to_sym] = value
320
+ end
321
+
322
+ result
323
+ ensure
324
+ Rdkafka::Bindings.rd_kafka_conf_dump_free(dump_ptr, count) if dump_ptr
325
+ Rdkafka::Bindings.rd_kafka_conf_destroy(config) if config
326
+ end
327
+
288
328
  # Error that is returned by the underlying rdkafka error if an invalid configuration option is present.
289
329
  class ConfigError < RuntimeError; end
290
330
 
@@ -364,10 +404,9 @@ module Rdkafka
364
404
  end
365
405
 
366
406
  # Redirect log to handle's queue
367
- Rdkafka::Bindings.rd_kafka_set_log_queue(
368
- handle,
369
- Rdkafka::Bindings.rd_kafka_queue_get_main(handle)
370
- )
407
+ main_queue = Rdkafka::Bindings.rd_kafka_queue_get_main(handle)
408
+ Rdkafka::Bindings.rd_kafka_set_log_queue(handle, main_queue)
409
+ Rdkafka::Bindings.rd_kafka_queue_destroy(main_queue)
371
410
 
372
411
  # Return handle which should be closed using rd_kafka_destroy after usage.
373
412
  handle
@@ -183,6 +183,11 @@ module Rdkafka
183
183
 
184
184
  @native_kafka.synchronize do |inner|
185
185
  Rdkafka::Bindings.rd_kafka_consumer_close(inner)
186
+
187
+ if @consumer_queue
188
+ Rdkafka::Bindings.rd_kafka_queue_destroy(@consumer_queue)
189
+ @consumer_queue = nil
190
+ end
186
191
  end
187
192
 
188
193
  @native_kafka.close
@@ -434,6 +439,8 @@ module Rdkafka
434
439
  end
435
440
 
436
441
  TopicPartitionList.from_native_tpl(tpl)
442
+ ensure
443
+ Rdkafka::Bindings.rd_kafka_topic_partition_list_destroy(tpl) if tpl
437
444
  end
438
445
 
439
446
  # Query broker for low (oldest/beginning) and high (newest/end) offsets for a partition.
@@ -779,6 +786,130 @@ module Rdkafka
779
786
  end
780
787
  end
781
788
 
789
+ # Poll for a batch of messages from the consumer queue in a single FFI call.
790
+ #
791
+ # This is more efficient than calling {#poll} in a loop because it crosses the FFI
792
+ # boundary only once to fetch up to `max_items` messages.
793
+ #
794
+ # The timeout controls how long to wait for the **first** message. Once any message
795
+ # is available, librdkafka fills the buffer with whatever is immediately ready and
796
+ # returns without further waiting.
797
+ #
798
+ # @param timeout_ms [Integer] Timeout waiting for the first message (-1 for infinite)
799
+ # @param max_items [Integer] Maximum number of messages to return per call
800
+ # @return [Array<Message>] Array of messages (empty if none available within timeout)
801
+ # @raise [RdkafkaError] When a consumed message contains an error
802
+ # @raise [ClosedConsumerError] When called on a closed consumer
803
+ def poll_batch(timeout_ms, max_items: 100)
804
+ closed_consumer_check(__method__)
805
+
806
+ buffer = batch_buffer(max_items)
807
+ messages = []
808
+
809
+ count = @native_kafka.with_inner do |_inner|
810
+ Rdkafka::Bindings.rd_kafka_consume_batch_queue(
811
+ consumer_queue,
812
+ timeout_ms,
813
+ buffer,
814
+ max_items
815
+ )
816
+ end
817
+
818
+ return messages if count <= 0
819
+
820
+ i = 0
821
+ begin
822
+ while i < count
823
+ ptr = buffer.get_pointer(i * FFI::Pointer.size)
824
+
825
+ if ptr.null?
826
+ i += 1
827
+ next
828
+ end
829
+
830
+ native_message = Rdkafka::Bindings::Message.new(ptr)
831
+
832
+ if native_message[:err] != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
833
+ raise Rdkafka::RdkafkaError.new(native_message[:err])
834
+ end
835
+
836
+ messages << Rdkafka::Consumer::Message.new(native_message)
837
+ Rdkafka::Bindings.rd_kafka_message_destroy(ptr)
838
+ i += 1
839
+ end
840
+ ensure
841
+ while i < count
842
+ ptr = buffer.get_pointer(i * FFI::Pointer.size)
843
+ Rdkafka::Bindings.rd_kafka_message_destroy(ptr) unless ptr.null?
844
+ i += 1
845
+ end
846
+ end
847
+
848
+ messages
849
+ end
850
+
851
+ # Poll for a batch of messages without releasing the GVL (Global VM Lock).
852
+ #
853
+ # This is more efficient than {#poll_batch} for non-blocking poll(0) calls,
854
+ # particularly useful in fiber scheduler contexts where GVL release/reacquire
855
+ # overhead is wasteful since we don't expect to wait.
856
+ #
857
+ # @note Since the GVL is not released, a non-zero timeout_ms will block all Ruby
858
+ # threads/fibers for the duration. Use {#poll_batch} if you need a blocking wait.
859
+ #
860
+ # @param timeout_ms [Integer] Timeout waiting for the first message (default: 0 for non-blocking)
861
+ # @param max_items [Integer] Maximum number of messages to return per call
862
+ # @return [Array<Message>] Array of messages (empty if none available within timeout)
863
+ # @raise [RdkafkaError] When a consumed message contains an error
864
+ # @raise [ClosedConsumerError] When called on a closed consumer
865
+ def poll_batch_nb(timeout_ms = 0, max_items: 100)
866
+ closed_consumer_check(__method__)
867
+
868
+ buffer = batch_buffer(max_items)
869
+ messages = []
870
+
871
+ count = @native_kafka.with_inner do |_inner|
872
+ Rdkafka::Bindings.rd_kafka_consume_batch_queue_nb(
873
+ consumer_queue,
874
+ timeout_ms,
875
+ buffer,
876
+ max_items
877
+ )
878
+ end
879
+
880
+ return messages if count <= 0
881
+
882
+ i = 0
883
+ begin
884
+ while i < count
885
+ ptr = buffer.get_pointer(i * FFI::Pointer.size)
886
+
887
+ if ptr.null?
888
+ i += 1
889
+ next
890
+ end
891
+
892
+ native_message = Rdkafka::Bindings::Message.new(ptr)
893
+
894
+ if native_message[:err] != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
895
+ raise Rdkafka::RdkafkaError.new(native_message[:err])
896
+ end
897
+
898
+ messages << Rdkafka::Consumer::Message.new(native_message)
899
+ Rdkafka::Bindings.rd_kafka_message_destroy(ptr)
900
+ i += 1
901
+ end
902
+ ensure
903
+ while i < count
904
+ ptr = buffer.get_pointer(i * FFI::Pointer.size)
905
+ Rdkafka::Bindings.rd_kafka_message_destroy(ptr) unless ptr.null?
906
+ i += 1
907
+ end
908
+ end
909
+
910
+ messages
911
+ end
912
+
782
913
  # Poll for new messages and yield for each received one. Iteration
783
914
  # will end when the consumer is closed.
784
915
  #
@@ -851,5 +982,24 @@ module Rdkafka
851
982
  def closed_consumer_check(method)
852
983
  raise Rdkafka::ClosedConsumerError.new(method) if closed?
853
984
  end
985
+
986
+ # Returns the consumer queue pointer, lazily initialized
987
+ # @return [FFI::Pointer] consumer queue handle
988
+ def consumer_queue
989
+ @consumer_queue ||= @native_kafka.with_inner do |inner|
990
+ Rdkafka::Bindings.rd_kafka_queue_get_consumer(inner)
991
+ end
992
+ end
993
+
994
+ # Returns a reusable FFI buffer for batch polling, growing if needed
995
+ # @param max_items [Integer] minimum buffer capacity
996
+ # @return [FFI::MemoryPointer] pointer buffer
997
+ def batch_buffer(max_items)
998
+ if @batch_buffer.nil? || @batch_buffer_size < max_items
999
+ @batch_buffer = FFI::MemoryPointer.new(:pointer, max_items)
1000
+ @batch_buffer_size = max_items
1001
+ end
1002
+ @batch_buffer
1003
+ end
854
1004
  end
855
1005
  end
@@ -2,9 +2,9 @@
2
2
 
3
3
  module Rdkafka
4
4
  # Current rdkafka-ruby gem version
5
- VERSION = "0.26.0"
5
+ VERSION = "0.27.0"
6
6
  # Target librdkafka version to be used
7
- LIBRDKAFKA_VERSION = "2.13.2"
7
+ LIBRDKAFKA_VERSION = "2.14.0"
8
8
  # SHA256 hash of the librdkafka source tarball for verification
9
- LIBRDKAFKA_SOURCE_SHA256 = "14972092e4115f6e99f798a7cb420cbf6daa0c73502b3c52ae42fb5b418eea8f"
9
+ LIBRDKAFKA_SOURCE_SHA256 = "c05c03ef00a13a8463fac3e8918c04843c416f11ced58c889d806a88ca92cf99"
10
10
  end
data/renovate.json CHANGED
@@ -90,5 +90,8 @@
90
90
  ],
91
91
  "labels": [
92
92
  "dependencies"
93
- ]
93
+ ],
94
+ "lockFileMaintenance": {
95
+ "enabled": true
96
+ }
94
97
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rdkafka
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.26.0
4
+ version: 0.27.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thijs Cadier
@@ -96,7 +96,7 @@ files:
96
96
  - README.md
97
97
  - Rakefile
98
98
  - bin/verify_kafka_warnings
99
- - dist/librdkafka-2.13.2.tar.gz
99
+ - dist/librdkafka-2.14.0.tar.gz
100
100
  - dist/patches/rdkafka_global_init.patch
101
101
  - docker-compose-ssl.yml
102
102
  - docker-compose.yml