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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/Gemfile +8 -0
  4. data/Gemfile.lint +14 -0
  5. data/Gemfile.lint.lock +123 -0
  6. data/README.md +2 -1
  7. data/Rakefile +21 -21
  8. data/docker-compose-ssl.yml +1 -1
  9. data/docker-compose.yml +1 -1
  10. data/ext/librdkafka.so +0 -0
  11. data/lib/rdkafka/abstract_handle.rb +23 -5
  12. data/lib/rdkafka/admin/acl_binding_result.rb +5 -5
  13. data/lib/rdkafka/admin/config_resource_binding_result.rb +1 -0
  14. data/lib/rdkafka/admin/create_acl_handle.rb +7 -4
  15. data/lib/rdkafka/admin/create_acl_report.rb +3 -2
  16. data/lib/rdkafka/admin/create_partitions_handle.rb +8 -5
  17. data/lib/rdkafka/admin/create_partitions_report.rb +1 -0
  18. data/lib/rdkafka/admin/create_topic_handle.rb +8 -5
  19. data/lib/rdkafka/admin/create_topic_report.rb +3 -0
  20. data/lib/rdkafka/admin/delete_acl_handle.rb +9 -6
  21. data/lib/rdkafka/admin/delete_acl_report.rb +5 -3
  22. data/lib/rdkafka/admin/delete_groups_handle.rb +10 -5
  23. data/lib/rdkafka/admin/delete_groups_report.rb +3 -0
  24. data/lib/rdkafka/admin/delete_topic_handle.rb +8 -5
  25. data/lib/rdkafka/admin/delete_topic_report.rb +3 -0
  26. data/lib/rdkafka/admin/describe_acl_handle.rb +9 -6
  27. data/lib/rdkafka/admin/describe_acl_report.rb +5 -3
  28. data/lib/rdkafka/admin/describe_configs_handle.rb +7 -4
  29. data/lib/rdkafka/admin/describe_configs_report.rb +7 -1
  30. data/lib/rdkafka/admin/incremental_alter_configs_handle.rb +7 -4
  31. data/lib/rdkafka/admin/incremental_alter_configs_report.rb +7 -1
  32. data/lib/rdkafka/admin.rb +194 -132
  33. data/lib/rdkafka/bindings.rb +155 -107
  34. data/lib/rdkafka/callbacks.rb +81 -21
  35. data/lib/rdkafka/config.rb +36 -24
  36. data/lib/rdkafka/consumer/headers.rb +3 -2
  37. data/lib/rdkafka/consumer/message.rb +12 -11
  38. data/lib/rdkafka/consumer/partition.rb +8 -4
  39. data/lib/rdkafka/consumer/topic_partition_list.rb +18 -18
  40. data/lib/rdkafka/consumer.rb +247 -42
  41. data/lib/rdkafka/defaults.rb +106 -0
  42. data/lib/rdkafka/error.rb +28 -13
  43. data/lib/rdkafka/helpers/oauth.rb +11 -6
  44. data/lib/rdkafka/helpers/time.rb +5 -0
  45. data/lib/rdkafka/metadata.rb +45 -21
  46. data/lib/rdkafka/native_kafka.rb +89 -4
  47. data/lib/rdkafka/producer/delivery_handle.rb +5 -5
  48. data/lib/rdkafka/producer/delivery_report.rb +8 -4
  49. data/lib/rdkafka/producer/partitions_count_cache.rb +29 -19
  50. data/lib/rdkafka/producer.rb +165 -79
  51. data/lib/rdkafka/version.rb +6 -3
  52. data/lib/rdkafka.rb +1 -0
  53. data/package-lock.json +331 -0
  54. data/package.json +9 -0
  55. data/rdkafka.gemspec +39 -47
  56. data/renovate.json +22 -8
  57. metadata +7 -86
@@ -14,10 +14,9 @@ module Rdkafka
14
14
  # then. Since the partitions count can only grow and should be same for all consumers and
15
15
  # producers, we can use a global cache as long as we ensure that updates only move up.
16
16
  #
17
+ # @return [Rdkafka::Producer::PartitionsCountCache]
17
18
  # @note It is critical to remember, that not all users may have statistics callbacks enabled,
18
19
  # hence we should not make assumption that this cache is always updated from the stats.
19
- #
20
- # @return [Rdkafka::Producer::PartitionsCountCache]
21
20
  def self.partitions_count_cache
22
21
  @@partitions_count_cache
23
22
  end
@@ -64,12 +63,13 @@ module Rdkafka
64
63
  end
65
64
 
66
65
  # Sets alternative set of configuration details that can be set per topic
67
- # @note It is not allowed to re-set the same topic config twice because of the underlying
68
- # librdkafka caching
66
+ #
69
67
  # @param topic [String] The topic name
70
68
  # @param config [Hash] config we want to use per topic basis
71
69
  # @param config_hash [Integer] hash of the config. We expect it here instead of computing it,
72
70
  # because it is already computed during the retrieval attempt in the `#produce` flow.
71
+ # @note It is not allowed to re-set the same topic config twice because of the underlying
72
+ # librdkafka caching
73
73
  def set_topic_config(topic, config, config_hash)
74
74
  # Ensure lock on topic reference just in case
75
75
  @native_kafka.with_inner do |inner|
@@ -80,25 +80,25 @@ module Rdkafka
80
80
 
81
81
  # If config is empty, we create an empty reference that will be used with defaults
82
82
  rd_topic_config = if config.empty?
83
- nil
84
- else
85
- Rdkafka::Bindings.rd_kafka_topic_conf_new.tap do |topic_config|
86
- config.each do |key, value|
87
- error_buffer = FFI::MemoryPointer.new(:char, 256)
88
- result = Rdkafka::Bindings.rd_kafka_topic_conf_set(
89
- topic_config,
90
- key.to_s,
91
- value.to_s,
92
- error_buffer,
93
- 256
94
- )
95
-
96
- unless result == :config_ok
97
- raise Config::ConfigError.new(error_buffer.read_string)
98
- end
99
- end
100
- end
101
- end
83
+ nil
84
+ else
85
+ Rdkafka::Bindings.rd_kafka_topic_conf_new.tap do |topic_config|
86
+ config.each do |key, value|
87
+ error_buffer = FFI::MemoryPointer.new(:char, 256)
88
+ result = Rdkafka::Bindings.rd_kafka_topic_conf_set(
89
+ topic_config,
90
+ key.to_s,
91
+ value.to_s,
92
+ error_buffer,
93
+ 256
94
+ )
95
+
96
+ unless result == :config_ok
97
+ raise Config::ConfigError.new(error_buffer.read_string)
98
+ end
99
+ end
100
+ end
101
+ end
102
102
 
103
103
  topic_handle = Bindings.rd_kafka_topic_new(inner, topic, rd_topic_config)
104
104
 
@@ -122,11 +122,30 @@ module Rdkafka
122
122
  end
123
123
  end
124
124
 
125
+ # Enable IO event notifications for fiber scheduler integration
126
+ # When delivery confirmations arrive, librdkafka will write to your FD
127
+ #
128
+ # @param fd [Integer] file descriptor to signal (from IO.pipe or eventfd)
129
+ # @param payload [String] data to write to fd (default: "\x01")
130
+ # @return [nil]
131
+ # @raise [ClosedInnerError] when the producer is closed
132
+ def enable_queue_io_events(fd, payload = "\x01")
133
+ @native_kafka.enable_main_queue_io_events(fd, payload)
134
+ end
135
+
136
+ # Enable IO event notifications for background events
137
+ # @param fd [Integer] file descriptor to signal (from IO.pipe or eventfd)
138
+ # @param payload [String] data to write to fd (default: "\x01")
139
+ # @return [nil]
140
+ # @raise [ClosedInnerError] when the producer is closed
141
+ def enable_background_queue_io_events(fd, payload = "\x01")
142
+ @native_kafka.enable_background_queue_io_events(fd, payload)
143
+ end
144
+
125
145
  # Set a callback that will be called every time a message is successfully produced.
126
146
  # The callback is called with a {DeliveryReport} and {DeliveryHandle}
127
147
  #
128
- # @param callback [Proc, #call] The callback
129
- #
148
+ # @param callback [Proc, #call] callable object to handle delivery reports
130
149
  # @return [nil]
131
150
  def delivery_callback=(callback)
132
151
  raise TypeError.new("Callback has to be callable") unless callback.respond_to?(:call)
@@ -168,7 +187,7 @@ module Rdkafka
168
187
  # should be no other errors.
169
188
  #
170
189
  # @note For `timed_out` we do not raise an error to keep it backwards compatible
171
- def flush(timeout_ms=5_000)
190
+ def flush(timeout_ms = Defaults::PRODUCER_FLUSH_TIMEOUT_MS)
172
191
  closed_producer_check(__method__)
173
192
 
174
193
  code = nil
@@ -208,11 +227,76 @@ module Rdkafka
208
227
  code.zero? || raise(Rdkafka::RdkafkaError.new(code))
209
228
 
210
229
  # Wait for the purge to affect everything
211
- sleep(0.001) until flush(100)
230
+ sleep(Defaults::PRODUCER_PURGE_SLEEP_INTERVAL_MS / 1000.0) until flush(Defaults::PRODUCER_PURGE_FLUSH_TIMEOUT_MS)
212
231
 
213
232
  true
214
233
  end
215
234
 
235
+ # Returns the number of messages and requests waiting to be sent to the broker as well as
236
+ # delivery reports queued for the application.
237
+ #
238
+ # This provides visibility into the producer's internal queue depth, useful for:
239
+ # - Monitoring producer backpressure
240
+ # - Implementing custom flow control
241
+ # - Debugging message delivery issues
242
+ # - Graceful shutdown logic (wait until queue is empty)
243
+ #
244
+ # @return [Integer] the number of messages in the queue
245
+ # @raise [Rdkafka::ClosedProducerError] if called on a closed producer
246
+ #
247
+ # @note This method is thread-safe as it uses the @native_kafka.with_inner synchronization
248
+ #
249
+ # @example
250
+ # producer.queue_size #=> 42
251
+ def queue_size
252
+ closed_producer_check(__method__)
253
+
254
+ @native_kafka.with_inner do |inner|
255
+ Rdkafka::Bindings.rd_kafka_outq_len(inner)
256
+ end
257
+ end
258
+
259
+ alias_method :queue_length, :queue_size
260
+
261
+ # Polls for events in a non-blocking loop, yielding the count after each iteration.
262
+ #
263
+ # This method processes delivery callbacks in a single GVL/mutex session, which is more
264
+ # efficient than repeated individual polls. It uses non-blocking polls internally
265
+ # (no GVL release between polls).
266
+ #
267
+ # Yields the count of events processed after each poll iteration, allowing the caller
268
+ # to implement timeout or other termination logic by returning `:stop`.
269
+ #
270
+ # @yield [count] Called after each poll iteration
271
+ # @yieldparam count [Integer] Number of events processed in this iteration
272
+ # @yieldreturn [Symbol, Object] Return `:stop` to break the loop, any other value continues
273
+ # @return [nil]
274
+ # @raise [Rdkafka::ClosedProducerError] if called on a closed producer
275
+ #
276
+ # @note This method holds the inner lock until the queue is empty or `:stop` is returned.
277
+ # Other producer operations (produce, close, etc.) will wait until this method returns.
278
+ # @note This method is thread-safe as it uses @native_kafka.with_inner synchronization
279
+ #
280
+ # @example Drain all pending callbacks
281
+ # producer.events_poll_nb_each { |_count| }
282
+ #
283
+ # @example With timeout control
284
+ # deadline = monotonic_now + timeout_ms
285
+ # producer.events_poll_nb_each do |_count|
286
+ # :stop if monotonic_now >= deadline
287
+ # end
288
+ def events_poll_nb_each
289
+ closed_producer_check(__method__)
290
+
291
+ @native_kafka.with_inner do |inner|
292
+ loop do
293
+ count = Rdkafka::Bindings.rd_kafka_poll_nb(inner, 0)
294
+ break if count.zero?
295
+ break if yield(count) == :stop
296
+ end
297
+ end
298
+ end
299
+
216
300
  # Partition count for a given topic.
217
301
  #
218
302
  # @param topic [String] The topic name.
@@ -237,13 +321,13 @@ module Rdkafka
237
321
  topic_metadata = ::Rdkafka::Metadata.new(inner, topic).topics&.first
238
322
  end
239
323
 
240
- topic_metadata ? topic_metadata[:partition_count] : -1
324
+ topic_metadata ? topic_metadata[:partition_count] : Rdkafka::Bindings::RD_KAFKA_PARTITION_UA
241
325
  end
242
326
  rescue Rdkafka::RdkafkaError => e
243
327
  # If the topic does not exist, it will be created or if not allowed another error will be
244
- # raised. We here return -1 so this can happen without early error happening on metadata
245
- # discovery.
246
- return -1 if e.code == :unknown_topic_or_part
328
+ # raised. We here return RD_KAFKA_PARTITION_UA so this can happen without early error
329
+ # happening on metadata discovery.
330
+ return Rdkafka::Bindings::RD_KAFKA_PARTITION_UA if e.code == :unknown_topic_or_part
247
331
 
248
332
  raise(e)
249
333
  end
@@ -254,14 +338,15 @@ module Rdkafka
254
338
  # When a timestamp is provided this is used instead of the auto-generated timestamp.
255
339
  #
256
340
  # @param topic [String] The topic to produce to
257
- # @param payload [String,nil] The message's payload
258
- # @param key [String, nil] The message's key
259
- # @param partition [Integer,nil] Optional partition to produce to
341
+ # @param payload [String, nil]
342
+ # @param key [String, nil]
343
+ # @param partition [Integer, nil] Optional partition to produce to
260
344
  # @param partition_key [String, nil] Optional partition key based on which partition assignment can happen
261
- # @param timestamp [Time,Integer,nil] Optional timestamp of this message. Integer timestamp is in milliseconds since Jan 1 1970.
262
- # @param headers [Hash<String,String|Array<String>>] Optional message headers. Values can be either a single string or an array of strings to support duplicate headers per KIP-82
345
+ # @param timestamp [Time, Integer, nil] Optional timestamp of this message. Integer timestamp is in milliseconds since Jan 1 1970.
346
+ # @param headers [Hash{String => String, Array<String>}] Optional message headers. Values can be either a single string or an array of strings to support duplicate headers per KIP-82
263
347
  # @param label [Object, nil] a label that can be assigned when producing a message that will be part of the delivery handle and the delivery report
264
348
  # @param topic_config [Hash] topic config for given message dispatch. Allows to send messages to topics with different configuration
349
+ # @param partitioner [String] name of the partitioner to use
265
350
  #
266
351
  # @return [DeliveryHandle] Delivery handle that can be used to wait for the result of producing this message
267
352
  #
@@ -284,17 +369,17 @@ module Rdkafka
284
369
 
285
370
  # Get payload length
286
371
  payload_size = if payload.nil?
287
- 0
288
- else
289
- payload.bytesize
290
- end
372
+ 0
373
+ else
374
+ payload.bytesize
375
+ end
291
376
 
292
377
  # Get key length
293
378
  key_size = if key.nil?
294
- 0
295
- else
296
- key.bytesize
297
- end
379
+ 0
380
+ else
381
+ key.bytesize
382
+ end
298
383
 
299
384
  topic_config_hash = topic_config.hash
300
385
 
@@ -311,36 +396,39 @@ module Rdkafka
311
396
  selected_partitioner = @topics_configs.dig(topic, topic_config_hash, :partitioner) || partitioner
312
397
 
313
398
  # If the topic is not present, set to -1
314
- partition = Rdkafka::Bindings.partitioner(
315
- topic_ref,
316
- partition_key,
317
- partition_count,
318
- selected_partitioner) if partition_count.positive?
399
+ if partition_count.positive?
400
+ partition = Rdkafka::Bindings.partitioner(
401
+ topic_ref,
402
+ partition_key,
403
+ partition_count,
404
+ selected_partitioner
405
+ )
406
+ end
319
407
  end
320
408
 
321
- # If partition is nil, use -1 to let librdafka set the partition randomly or
409
+ # If partition is nil, use RD_KAFKA_PARTITION_UA to let librdafka set the partition randomly or
322
410
  # based on the key when present.
323
- partition ||= -1
411
+ partition ||= Rdkafka::Bindings::RD_KAFKA_PARTITION_UA
324
412
 
325
413
  # If timestamp is nil use 0 and let Kafka set one. If an integer or time
326
414
  # use it.
327
415
  raw_timestamp = if timestamp.nil?
328
- 0
329
- elsif timestamp.is_a?(Integer)
330
- timestamp
331
- elsif timestamp.is_a?(Time)
332
- (timestamp.to_i * 1000) + (timestamp.usec / 1000)
333
- else
334
- raise TypeError.new("Timestamp has to be nil, an Integer or a Time")
335
- end
416
+ 0
417
+ elsif timestamp.is_a?(Integer)
418
+ timestamp
419
+ elsif timestamp.is_a?(Time)
420
+ (timestamp.to_i * 1000) + (timestamp.usec / 1000)
421
+ else
422
+ raise TypeError.new("Timestamp has to be nil, an Integer or a Time")
423
+ end
336
424
 
337
425
  delivery_handle = DeliveryHandle.new
338
426
  delivery_handle.label = label
339
427
  delivery_handle.topic = topic
340
428
  delivery_handle[:pending] = true
341
- delivery_handle[:response] = -1
342
- delivery_handle[:partition] = -1
343
- delivery_handle[:offset] = -1
429
+ delivery_handle[:response] = Rdkafka::Bindings::RD_KAFKA_PARTITION_UA
430
+ delivery_handle[:partition] = Rdkafka::Bindings::RD_KAFKA_PARTITION_UA
431
+ delivery_handle[:offset] = Rdkafka::Bindings::RD_KAFKA_PARTITION_UA
344
432
  DeliveryHandle.register(delivery_handle)
345
433
 
346
434
  args = [
@@ -350,29 +438,27 @@ module Rdkafka
350
438
  :int, Rdkafka::Bindings::RD_KAFKA_VTYPE_KEY, :buffer_in, key, :size_t, key_size,
351
439
  :int, Rdkafka::Bindings::RD_KAFKA_VTYPE_PARTITION, :int32, partition,
352
440
  :int, Rdkafka::Bindings::RD_KAFKA_VTYPE_TIMESTAMP, :int64, raw_timestamp,
353
- :int, Rdkafka::Bindings::RD_KAFKA_VTYPE_OPAQUE, :pointer, delivery_handle,
441
+ :int, Rdkafka::Bindings::RD_KAFKA_VTYPE_OPAQUE, :pointer, delivery_handle
354
442
  ]
355
443
 
356
- if headers
357
- headers.each do |key0, value0|
358
- key = key0.to_s
359
- if value0.is_a?(Array)
360
- # Handle array of values per KIP-82
361
- value0.each do |value|
362
- value = value.to_s
363
- args << :int << Rdkafka::Bindings::RD_KAFKA_VTYPE_HEADER
364
- args << :string << key
365
- args << :pointer << value
366
- args << :size_t << value.bytesize
367
- end
368
- else
369
- # Handle single value
370
- value = value0.to_s
444
+ headers&.each do |key0, value0|
445
+ key = key0.to_s
446
+ if value0.is_a?(Array)
447
+ # Handle array of values per KIP-82
448
+ value0.each do |value|
449
+ value = value.to_s
371
450
  args << :int << Rdkafka::Bindings::RD_KAFKA_VTYPE_HEADER
372
451
  args << :string << key
373
452
  args << :pointer << value
374
453
  args << :size_t << value.bytesize
375
454
  end
455
+ else
456
+ # Handle single value
457
+ value = value0.to_s
458
+ args << :int << Rdkafka::Bindings::RD_KAFKA_VTYPE_HEADER
459
+ args << :string << key
460
+ args << :pointer << value
461
+ args << :size_t << value.bytesize
376
462
  end
377
463
  end
378
464
 
@@ -387,7 +473,7 @@ module Rdkafka
387
473
  end
388
474
 
389
475
  # Raise error if the produce call was not successful
390
- if response != 0
476
+ if response != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
391
477
  DeliveryHandle.remove(delivery_handle.to_ptr.address)
392
478
  raise RdkafkaError.new(response)
393
479
  end
@@ -1,7 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rdkafka
4
- VERSION = "0.24.2"
5
- LIBRDKAFKA_VERSION = "2.11.1"
6
- LIBRDKAFKA_SOURCE_SHA256 = "a2c87186b081e2705bb7d5338d5a01bc88d43273619b372ccb7bb0d264d0ca9f"
4
+ # Current rdkafka-ruby gem version
5
+ VERSION = "0.25.1"
6
+ # Target librdkafka version to be used
7
+ LIBRDKAFKA_VERSION = "2.12.1"
8
+ # SHA256 hash of the librdkafka source tarball for verification
9
+ LIBRDKAFKA_SOURCE_SHA256 = "ec103fa05cb0f251e375f6ea0b6112cfc9d0acd977dc5b69fdc54242ba38a16f"
7
10
  end
data/lib/rdkafka.rb CHANGED
@@ -8,6 +8,7 @@ require "json"
8
8
  require "rdkafka/version"
9
9
  require "rdkafka/helpers/time"
10
10
  require "rdkafka/helpers/oauth"
11
+ require "rdkafka/defaults"
11
12
  require "rdkafka/abstract_handle"
12
13
  require "rdkafka/admin"
13
14
  require "rdkafka/admin/create_topic_handle"