karafka-rdkafka 0.28.0-aarch64-linux-gnu → 0.28.2-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 +46 -30
- data/README.md +3 -3
- data/ext/librdkafka.so +0 -0
- data/lib/rdkafka/admin/create_acl_report.rb +2 -1
- data/lib/rdkafka/admin/delete_records_handle.rb +31 -0
- data/lib/rdkafka/admin/delete_records_report.rb +25 -0
- data/lib/rdkafka/admin/describe_acl_report.rb +2 -1
- data/lib/rdkafka/admin/list_consumer_groups_handle.rb +31 -0
- data/lib/rdkafka/admin/list_consumer_groups_report.rb +83 -0
- data/lib/rdkafka/admin.rb +317 -90
- data/lib/rdkafka/bindings.rb +52 -13
- data/lib/rdkafka/callbacks/delete_records_handler.rb +38 -0
- data/lib/rdkafka/callbacks/list_consumer_groups_handler.rb +39 -0
- data/lib/rdkafka/callbacks.rb +4 -2
- data/lib/rdkafka/clients.rb +114 -0
- data/lib/rdkafka/config.rb +103 -56
- data/lib/rdkafka/consumer/allocator.rb +38 -0
- data/lib/rdkafka/consumer/topic_partition_list.rb +16 -12
- data/lib/rdkafka/consumer.rb +81 -76
- data/lib/rdkafka/defaults.rb +11 -2
- data/lib/rdkafka/error.rb +2 -1
- data/lib/rdkafka/helpers/oauth.rb +10 -5
- data/lib/rdkafka/native_kafka.rb +20 -7
- data/lib/rdkafka/producer/delivery_handle.rb +1 -2
- data/lib/rdkafka/producer/testing.rb +2 -1
- data/lib/rdkafka/producer.rb +28 -20
- data/lib/rdkafka/version.rb +1 -1
- data/lib/rdkafka.rb +10 -0
- data/package-lock.json +6 -6
- data/renovate.json +1 -1
- metadata +10 -2
data/lib/rdkafka/admin.rb
CHANGED
|
@@ -108,10 +108,6 @@ module Rdkafka
|
|
|
108
108
|
# @return [nil]
|
|
109
109
|
# @raise [Rdkafka::ClosedAdminError] if called on a closed admin client
|
|
110
110
|
#
|
|
111
|
-
# @note This method holds the inner lock until the queue is empty or `:stop` is returned.
|
|
112
|
-
# Other admin operations will wait until this method returns.
|
|
113
|
-
# @note This method is thread-safe as it uses @native_kafka.with_inner synchronization
|
|
114
|
-
#
|
|
115
111
|
# @example Drain all pending events
|
|
116
112
|
# admin.events_poll_nb_each { |_count| }
|
|
117
113
|
#
|
|
@@ -120,6 +116,9 @@ module Rdkafka
|
|
|
120
116
|
# admin.events_poll_nb_each do |_count|
|
|
121
117
|
# :stop if monotonic_now >= deadline
|
|
122
118
|
# end
|
|
119
|
+
# @note This method holds the inner lock until the queue is empty or `:stop` is returned.
|
|
120
|
+
# Other admin operations will wait until this method returns.
|
|
121
|
+
# @note This method is thread-safe as it uses @native_kafka.with_inner synchronization
|
|
123
122
|
def events_poll_nb_each
|
|
124
123
|
closed_admin_check(__method__)
|
|
125
124
|
|
|
@@ -283,6 +282,172 @@ module Rdkafka
|
|
|
283
282
|
delete_groups_handle
|
|
284
283
|
end
|
|
285
284
|
|
|
285
|
+
# Deletes all messages in the given partitions up to (but not including) the given offset.
|
|
286
|
+
#
|
|
287
|
+
# The programmatic equivalent of `kafka-delete-records.sh`. Useful for GDPR/right-to-erasure
|
|
288
|
+
# compliance, clearing out poison messages, or retention cleanup outside of the configured
|
|
289
|
+
# time/size-based retention policy.
|
|
290
|
+
#
|
|
291
|
+
# @param topic_partition_offsets [Hash{String => Array<Hash>}] hash mapping topic names to
|
|
292
|
+
# arrays of partition delete specifications. Each specification is a hash with:
|
|
293
|
+
# - `:partition` [Integer] partition number
|
|
294
|
+
# - `:offset` [Symbol, Integer] delete all messages before this offset (exclusive) - an
|
|
295
|
+
# integer offset, or `:end` to delete all data currently in the partition
|
|
296
|
+
#
|
|
297
|
+
# @return [DeleteRecordsHandle] handle that can be used to wait for the result
|
|
298
|
+
# @raise [RdkafkaError] when deleting records fails
|
|
299
|
+
#
|
|
300
|
+
# @example Delete all messages before offset 100 in one partition, and all data in another
|
|
301
|
+
# report = admin.delete_records(
|
|
302
|
+
# "my_topic" => [
|
|
303
|
+
# { partition: 0, offset: 100 },
|
|
304
|
+
# { partition: 1, offset: :end }
|
|
305
|
+
# ]
|
|
306
|
+
# ).wait(max_wait_timeout_ms: 15_000)
|
|
307
|
+
#
|
|
308
|
+
# report.offsets.to_h
|
|
309
|
+
# # => { "my_topic" => [#<Partition @partition=0, @offset=100, @err=0>, ...] }
|
|
310
|
+
def delete_records(topic_partition_offsets)
|
|
311
|
+
closed_admin_check(__method__)
|
|
312
|
+
|
|
313
|
+
parsed = topic_partition_offsets.flat_map do |topic, partitions|
|
|
314
|
+
partitions.map do |spec|
|
|
315
|
+
offset = spec.fetch(:offset)
|
|
316
|
+
|
|
317
|
+
native_offset = case offset
|
|
318
|
+
when :end then Rdkafka::Bindings::RD_KAFKA_OFFSET_END
|
|
319
|
+
when Integer then offset
|
|
320
|
+
else
|
|
321
|
+
raise ArgumentError, "Unknown offset specification: #{offset.inspect}"
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
[topic, spec.fetch(:partition), native_offset]
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
tpl = Rdkafka::Bindings.rd_kafka_topic_partition_list_new(parsed.size)
|
|
329
|
+
|
|
330
|
+
parsed.each do |topic, partition, native_offset|
|
|
331
|
+
Rdkafka::Bindings.rd_kafka_topic_partition_list_add(tpl, topic, partition)
|
|
332
|
+
Rdkafka::Bindings.rd_kafka_topic_partition_list_set_offset(tpl, topic, partition, native_offset)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# rd_kafka_DeleteRecords_new copies the tpl it is given, so it does not need to (and must
|
|
336
|
+
# not) outlive this call - it is destroyed below, independently of the wrapping
|
|
337
|
+
# DeleteRecords_t object.
|
|
338
|
+
delete_records_ptr = Rdkafka::Bindings.rd_kafka_DeleteRecords_new(tpl)
|
|
339
|
+
Rdkafka::Bindings.rd_kafka_topic_partition_list_destroy(tpl)
|
|
340
|
+
|
|
341
|
+
pointer_array = [delete_records_ptr]
|
|
342
|
+
records_array_ptr = FFI::MemoryPointer.new(:pointer)
|
|
343
|
+
records_array_ptr.write_array_of_pointer(pointer_array)
|
|
344
|
+
|
|
345
|
+
# Get a pointer to the queue that our request will be enqueued on
|
|
346
|
+
queue_ptr = @native_kafka.with_inner do |inner|
|
|
347
|
+
Rdkafka::Bindings.rd_kafka_queue_get_background(inner)
|
|
348
|
+
end
|
|
349
|
+
if queue_ptr.null?
|
|
350
|
+
Rdkafka::Bindings.rd_kafka_DeleteRecords_destroy(delete_records_ptr)
|
|
351
|
+
raise Rdkafka::Config::ConfigError.new("rd_kafka_queue_get_background was NULL")
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Create and register the handle we will return to the caller
|
|
355
|
+
handle = DeleteRecordsHandle.new
|
|
356
|
+
handle[:pending] = true
|
|
357
|
+
handle[:response] = Rdkafka::Bindings::RD_KAFKA_PARTITION_UA
|
|
358
|
+
DeleteRecordsHandle.register(handle)
|
|
359
|
+
|
|
360
|
+
admin_options_ptr = @native_kafka.with_inner do |inner|
|
|
361
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_new(inner, Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_DELETERECORDS)
|
|
362
|
+
end
|
|
363
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, handle.to_ptr)
|
|
364
|
+
|
|
365
|
+
begin
|
|
366
|
+
@native_kafka.with_inner do |inner|
|
|
367
|
+
Rdkafka::Bindings.rd_kafka_DeleteRecords(
|
|
368
|
+
inner,
|
|
369
|
+
records_array_ptr,
|
|
370
|
+
1,
|
|
371
|
+
admin_options_ptr,
|
|
372
|
+
queue_ptr
|
|
373
|
+
)
|
|
374
|
+
end
|
|
375
|
+
rescue Exception
|
|
376
|
+
DeleteRecordsHandle.remove(handle.to_ptr.address)
|
|
377
|
+
raise
|
|
378
|
+
ensure
|
|
379
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_destroy(admin_options_ptr)
|
|
380
|
+
Rdkafka::Bindings.rd_kafka_queue_destroy(queue_ptr)
|
|
381
|
+
Rdkafka::Bindings.rd_kafka_DeleteRecords_destroy(delete_records_ptr)
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
handle
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# Lists consumer groups cluster-wide.
|
|
388
|
+
#
|
|
389
|
+
# librdkafka issues a single `ListConsumerGroups` request that is fanned out to every broker
|
|
390
|
+
# internally, so the result covers all consumer groups in the cluster, not only those
|
|
391
|
+
# coordinated by the connected broker.
|
|
392
|
+
#
|
|
393
|
+
# @return [ListConsumerGroupsHandle] handle that can be used to wait for the result
|
|
394
|
+
# @raise [RdkafkaError] when listing the consumer groups fails
|
|
395
|
+
#
|
|
396
|
+
# @example List every consumer group in the cluster and print its name and attributes
|
|
397
|
+
# report = admin.list_consumer_groups.wait(max_wait_timeout_ms: 15_000)
|
|
398
|
+
#
|
|
399
|
+
# report.groups.each do |group|
|
|
400
|
+
# puts "#{group[:group_id]} - #{group[:state_name]} " \
|
|
401
|
+
# "(simple: #{group[:is_simple_consumer_group]})"
|
|
402
|
+
# end
|
|
403
|
+
#
|
|
404
|
+
# # Any brokers that could not be reached are reported separately
|
|
405
|
+
# report.errors.each { |error| warn "partial listing error: #{error.message}" }
|
|
406
|
+
def list_consumer_groups
|
|
407
|
+
closed_admin_check(__method__)
|
|
408
|
+
|
|
409
|
+
# Get a pointer to the queue that our request will be enqueued on
|
|
410
|
+
queue_ptr = @native_kafka.with_inner do |inner|
|
|
411
|
+
Rdkafka::Bindings.rd_kafka_queue_get_background(inner)
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
if queue_ptr.null?
|
|
415
|
+
raise Rdkafka::Config::ConfigError.new("rd_kafka_queue_get_background was NULL")
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
# Create and register the handle we will return to the caller
|
|
419
|
+
handle = ListConsumerGroupsHandle.new
|
|
420
|
+
handle[:pending] = true
|
|
421
|
+
handle[:response] = Rdkafka::Bindings::RD_KAFKA_PARTITION_UA
|
|
422
|
+
ListConsumerGroupsHandle.register(handle)
|
|
423
|
+
|
|
424
|
+
admin_options_ptr = @native_kafka.with_inner do |inner|
|
|
425
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_new(
|
|
426
|
+
inner,
|
|
427
|
+
Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_LISTCONSUMERGROUPS
|
|
428
|
+
)
|
|
429
|
+
end
|
|
430
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, handle.to_ptr)
|
|
431
|
+
|
|
432
|
+
begin
|
|
433
|
+
@native_kafka.with_inner do |inner|
|
|
434
|
+
Rdkafka::Bindings.rd_kafka_ListConsumerGroups(
|
|
435
|
+
inner,
|
|
436
|
+
admin_options_ptr,
|
|
437
|
+
queue_ptr
|
|
438
|
+
)
|
|
439
|
+
end
|
|
440
|
+
rescue Exception
|
|
441
|
+
ListConsumerGroupsHandle.remove(handle.to_ptr.address)
|
|
442
|
+
raise
|
|
443
|
+
ensure
|
|
444
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_destroy(admin_options_ptr)
|
|
445
|
+
Rdkafka::Bindings.rd_kafka_queue_destroy(queue_ptr)
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
handle
|
|
449
|
+
end
|
|
450
|
+
|
|
286
451
|
# Deletes the named topic
|
|
287
452
|
#
|
|
288
453
|
# @param topic_name [String] name of the topic to delete
|
|
@@ -345,7 +510,8 @@ module Rdkafka
|
|
|
345
510
|
#
|
|
346
511
|
# @param topic_name [String] name of the topic
|
|
347
512
|
# @param partition_count [Integer] how many partitions we want to end up with for given topic
|
|
348
|
-
# @return [CreatePartitionsHandle] Create partitions handle that can be used to wait for the
|
|
513
|
+
# @return [CreatePartitionsHandle] Create partitions handle that can be used to wait for the
|
|
514
|
+
# result
|
|
349
515
|
# @raise [ConfigError] When the partition count or replication factor are out of valid range
|
|
350
516
|
# @raise [RdkafkaError] When the topic name is invalid or the topic already exists
|
|
351
517
|
# @raise [RdkafkaError] When the topic configuration is invalid
|
|
@@ -434,7 +600,8 @@ module Rdkafka
|
|
|
434
600
|
# @param permission_type [Integer] rd_kafka_AclPermissionType_t value:
|
|
435
601
|
# - RD_KAFKA_ACL_PERMISSION_TYPE_DENY = 2
|
|
436
602
|
# - RD_KAFKA_ACL_PERMISSION_TYPE_ALLOW = 3
|
|
437
|
-
# @return [CreateAclHandle] Create acl handle that can be used to wait for the result of
|
|
603
|
+
# @return [CreateAclHandle] Create acl handle that can be used to wait for the result of
|
|
604
|
+
# creating the acl
|
|
438
605
|
# @raise [RdkafkaError]
|
|
439
606
|
def create_acl(resource_type:, resource_name:, resource_pattern_type:, principal:, host:, operation:, permission_type:)
|
|
440
607
|
closed_admin_check(__method__)
|
|
@@ -535,7 +702,8 @@ module Rdkafka
|
|
|
535
702
|
# @param permission_type [Integer] rd_kafka_AclPermissionType_t value:
|
|
536
703
|
# - RD_KAFKA_ACL_PERMISSION_TYPE_DENY = 2
|
|
537
704
|
# - RD_KAFKA_ACL_PERMISSION_TYPE_ALLOW = 3
|
|
538
|
-
# @return [DeleteAclHandle] Delete acl handle that can be used to wait for the result of
|
|
705
|
+
# @return [DeleteAclHandle] Delete acl handle that can be used to wait for the result of
|
|
706
|
+
# deleting the acl
|
|
539
707
|
# @raise [RdkafkaError]
|
|
540
708
|
def delete_acl(resource_type:, resource_name:, resource_pattern_type:, principal:, host:, operation:, permission_type:)
|
|
541
709
|
closed_admin_check(__method__)
|
|
@@ -638,7 +806,8 @@ module Rdkafka
|
|
|
638
806
|
# @param permission_type [Integer] rd_kafka_AclPermissionType_t value:
|
|
639
807
|
# - RD_KAFKA_ACL_PERMISSION_TYPE_DENY = 2
|
|
640
808
|
# - RD_KAFKA_ACL_PERMISSION_TYPE_ALLOW = 3
|
|
641
|
-
# @return [DescribeAclHandle] Describe acl handle that can be used to wait for the result of
|
|
809
|
+
# @return [DescribeAclHandle] Describe acl handle that can be used to wait for the result of
|
|
810
|
+
# fetching acls
|
|
642
811
|
# @raise [RdkafkaError]
|
|
643
812
|
def describe_acl(resource_type:, resource_name:, resource_pattern_type:, principal:, host:, operation:, permission_type:)
|
|
644
813
|
closed_admin_check(__method__)
|
|
@@ -729,35 +898,64 @@ module Rdkafka
|
|
|
729
898
|
handle[:pending] = true
|
|
730
899
|
handle[:response] = Rdkafka::Bindings::RD_KAFKA_PARTITION_UA
|
|
731
900
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
901
|
+
# All native allocation happens inside the begin so a raise at any point (a null background
|
|
902
|
+
# queue, a non-String resource name making `from_string` raise mid-build, etc.) is cleaned up
|
|
903
|
+
# by the ensure rather than leaking the queue, the AdminOptions, the handle and the
|
|
904
|
+
# ConfigResources already built.
|
|
905
|
+
queue_ptr = nil
|
|
906
|
+
admin_options_ptr = nil
|
|
907
|
+
pointer_array = []
|
|
908
|
+
registered = false
|
|
735
909
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
910
|
+
begin
|
|
911
|
+
queue_ptr = @native_kafka.with_inner do |inner|
|
|
912
|
+
Rdkafka::Bindings.rd_kafka_queue_get_background(inner)
|
|
913
|
+
end
|
|
739
914
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_DESCRIBECONFIGS
|
|
744
|
-
)
|
|
745
|
-
end
|
|
915
|
+
if queue_ptr.null?
|
|
916
|
+
raise Rdkafka::Config::ConfigError.new("rd_kafka_queue_get_background was NULL")
|
|
917
|
+
end
|
|
746
918
|
|
|
747
|
-
|
|
748
|
-
|
|
919
|
+
admin_options_ptr = @native_kafka.with_inner do |inner|
|
|
920
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_new(
|
|
921
|
+
inner,
|
|
922
|
+
Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_DESCRIBECONFIGS
|
|
923
|
+
)
|
|
924
|
+
end
|
|
749
925
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
end
|
|
926
|
+
# A NULL AdminOptions would segfault the moment we set its opaque pointer, so reject it
|
|
927
|
+
# before registering the handle (the ensure frees the queue already acquired).
|
|
928
|
+
if admin_options_ptr.null?
|
|
929
|
+
raise Rdkafka::Config::ConfigError.new("rd_kafka_AdminOptions_new was NULL")
|
|
930
|
+
end
|
|
756
931
|
|
|
757
|
-
|
|
758
|
-
|
|
932
|
+
DescribeConfigsHandle.register(handle)
|
|
933
|
+
registered = true
|
|
934
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, handle.to_ptr)
|
|
935
|
+
|
|
936
|
+
# Build resources one at a time so a raise on a later element still leaves the earlier ones
|
|
937
|
+
# in pointer_array for the ensure to destroy.
|
|
938
|
+
parsed_resources.each do |resource_type, resource_name|
|
|
939
|
+
resource_ptr = Rdkafka::Bindings.rd_kafka_ConfigResource_new(
|
|
940
|
+
resource_type,
|
|
941
|
+
FFI::MemoryPointer.from_string(resource_name)
|
|
942
|
+
)
|
|
943
|
+
|
|
944
|
+
# librdkafka returns NULL for an empty resource name or a negative resource type. Passing
|
|
945
|
+
# NULL on to DescribeConfigs and then destroying it both dereference NULL and segfault, so
|
|
946
|
+
# reject it here (the ensure frees everything already built).
|
|
947
|
+
if resource_ptr.null?
|
|
948
|
+
raise Rdkafka::Config::ConfigError.new(
|
|
949
|
+
"rd_kafka_ConfigResource_new was NULL for #{resource_name.inspect} (type #{resource_type})"
|
|
950
|
+
)
|
|
951
|
+
end
|
|
952
|
+
|
|
953
|
+
pointer_array << resource_ptr
|
|
954
|
+
end
|
|
955
|
+
|
|
956
|
+
configs_array_ptr = FFI::MemoryPointer.new(:pointer, pointer_array.size)
|
|
957
|
+
configs_array_ptr.write_array_of_pointer(pointer_array)
|
|
759
958
|
|
|
760
|
-
begin
|
|
761
959
|
@native_kafka.with_inner do |inner|
|
|
762
960
|
Rdkafka::Bindings.rd_kafka_DescribeConfigs(
|
|
763
961
|
inner,
|
|
@@ -768,18 +966,20 @@ module Rdkafka
|
|
|
768
966
|
)
|
|
769
967
|
end
|
|
770
968
|
rescue Exception
|
|
771
|
-
DescribeConfigsHandle.remove(handle.to_ptr.address)
|
|
969
|
+
DescribeConfigsHandle.remove(handle.to_ptr.address) if registered
|
|
772
970
|
|
|
773
971
|
raise
|
|
774
972
|
ensure
|
|
775
|
-
|
|
776
|
-
|
|
973
|
+
if admin_options_ptr && !admin_options_ptr.null?
|
|
974
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_destroy(admin_options_ptr)
|
|
975
|
+
end
|
|
777
976
|
|
|
778
|
-
if
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
977
|
+
Rdkafka::Bindings.rd_kafka_queue_destroy(queue_ptr) if queue_ptr && !queue_ptr.null?
|
|
978
|
+
|
|
979
|
+
pointer_array.each do |config_resource_ptr|
|
|
980
|
+
next if config_resource_ptr.null?
|
|
981
|
+
|
|
982
|
+
Rdkafka::Bindings.rd_kafka_ConfigResource_destroy(config_resource_ptr)
|
|
783
983
|
end
|
|
784
984
|
end
|
|
785
985
|
|
|
@@ -818,61 +1018,86 @@ module Rdkafka
|
|
|
818
1018
|
handle[:pending] = true
|
|
819
1019
|
handle[:response] = Rdkafka::Bindings::RD_KAFKA_PARTITION_UA
|
|
820
1020
|
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
1021
|
+
# All native allocation happens inside the begin so a raise at any point (a null background
|
|
1022
|
+
# queue, a non-String resource name, or FFI marshaling of a config name/value failing while
|
|
1023
|
+
# adding configs) is cleaned up by the ensure rather than leaking the queue, the AdminOptions,
|
|
1024
|
+
# the handle and the ConfigResources already built.
|
|
1025
|
+
queue_ptr = nil
|
|
1026
|
+
admin_options_ptr = nil
|
|
1027
|
+
pointer_array = []
|
|
1028
|
+
registered = false
|
|
1029
|
+
add_error = nil
|
|
824
1030
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
1031
|
+
begin
|
|
1032
|
+
queue_ptr = @native_kafka.with_inner do |inner|
|
|
1033
|
+
Rdkafka::Bindings.rd_kafka_queue_get_background(inner)
|
|
1034
|
+
end
|
|
828
1035
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_INCREMENTALALTERCONFIGS
|
|
833
|
-
)
|
|
834
|
-
end
|
|
1036
|
+
if queue_ptr.null?
|
|
1037
|
+
raise Rdkafka::Config::ConfigError.new("rd_kafka_queue_get_background was NULL")
|
|
1038
|
+
end
|
|
835
1039
|
|
|
836
|
-
|
|
837
|
-
|
|
1040
|
+
admin_options_ptr = @native_kafka.with_inner do |inner|
|
|
1041
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_new(
|
|
1042
|
+
inner,
|
|
1043
|
+
Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_INCREMENTALALTERCONFIGS
|
|
1044
|
+
)
|
|
1045
|
+
end
|
|
838
1046
|
|
|
839
|
-
|
|
1047
|
+
# A NULL AdminOptions would segfault the moment we set its opaque pointer, so reject it
|
|
1048
|
+
# before registering the handle (the ensure frees the queue already acquired).
|
|
1049
|
+
if admin_options_ptr.null?
|
|
1050
|
+
raise Rdkafka::Config::ConfigError.new("rd_kafka_AdminOptions_new was NULL")
|
|
1051
|
+
end
|
|
840
1052
|
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
resource_type,
|
|
845
|
-
FFI::MemoryPointer.from_string(resource_name)
|
|
846
|
-
)
|
|
1053
|
+
IncrementalAlterConfigsHandle.register(handle)
|
|
1054
|
+
registered = true
|
|
1055
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, handle.to_ptr)
|
|
847
1056
|
|
|
848
|
-
|
|
849
|
-
#
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
# destroys the native error object) and raise it below, before the request is sent.
|
|
854
|
-
error_ptr = Bindings.rd_kafka_ConfigResource_add_incremental_config(
|
|
855
|
-
resource_ptr,
|
|
856
|
-
name,
|
|
857
|
-
op_type,
|
|
858
|
-
value
|
|
1057
|
+
parsed_resources.each do |resource_type, resource_name, configs|
|
|
1058
|
+
# First build the appropriate resource representation
|
|
1059
|
+
resource_ptr = Rdkafka::Bindings.rd_kafka_ConfigResource_new(
|
|
1060
|
+
resource_type,
|
|
1061
|
+
FFI::MemoryPointer.from_string(resource_name)
|
|
859
1062
|
)
|
|
860
1063
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
1064
|
+
# librdkafka returns NULL for an empty resource name or a negative resource type. Passing
|
|
1065
|
+
# NULL on to add_incremental_config/IncrementalAlterConfigs and then destroying it both
|
|
1066
|
+
# dereference NULL and segfault, so reject it here (the ensure frees everything built).
|
|
1067
|
+
if resource_ptr.null?
|
|
1068
|
+
raise Rdkafka::Config::ConfigError.new(
|
|
1069
|
+
"rd_kafka_ConfigResource_new was NULL for #{resource_name.inspect} (type #{resource_type})"
|
|
1070
|
+
)
|
|
1071
|
+
end
|
|
865
1072
|
|
|
866
|
-
|
|
1073
|
+
# Track it immediately so a raise while adding its configs still frees it via the ensure.
|
|
1074
|
+
pointer_array << resource_ptr
|
|
1075
|
+
|
|
1076
|
+
configs.each do |name, op_type, value|
|
|
1077
|
+
# rd_kafka_ConfigResource_add_incremental_config returns a non-NULL rd_kafka_error_t for
|
|
1078
|
+
# an invalid op_type, an empty/nil name, or a nil value on a non-delete op. The result
|
|
1079
|
+
# used to be ignored: the entry was silently dropped, the alter request still reported
|
|
1080
|
+
# success, and the error object leaked. Capture the first error (build_from_c also
|
|
1081
|
+
# destroys the native error object) and raise it below, before the request is sent.
|
|
1082
|
+
error_ptr = Bindings.rd_kafka_ConfigResource_add_incremental_config(
|
|
1083
|
+
resource_ptr,
|
|
1084
|
+
name,
|
|
1085
|
+
op_type,
|
|
1086
|
+
value
|
|
1087
|
+
)
|
|
1088
|
+
|
|
1089
|
+
error = Rdkafka::RdkafkaError.build_from_c(
|
|
1090
|
+
error_ptr,
|
|
1091
|
+
"rd_kafka_ConfigResource_add_incremental_config"
|
|
1092
|
+
)
|
|
1093
|
+
|
|
1094
|
+
add_error ||= error if error
|
|
1095
|
+
end
|
|
867
1096
|
end
|
|
868
1097
|
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
configs_array_ptr = FFI::MemoryPointer.new(:pointer, pointer_array.size)
|
|
873
|
-
configs_array_ptr.write_array_of_pointer(pointer_array)
|
|
1098
|
+
configs_array_ptr = FFI::MemoryPointer.new(:pointer, pointer_array.size)
|
|
1099
|
+
configs_array_ptr.write_array_of_pointer(pointer_array)
|
|
874
1100
|
|
|
875
|
-
begin
|
|
876
1101
|
# Raise only after the full array is built so the ensure below frees every ConfigResource
|
|
877
1102
|
# we created, and before the request is sent so a rejected entry can't report success.
|
|
878
1103
|
raise add_error if add_error
|
|
@@ -887,18 +1112,20 @@ module Rdkafka
|
|
|
887
1112
|
)
|
|
888
1113
|
end
|
|
889
1114
|
rescue Exception
|
|
890
|
-
IncrementalAlterConfigsHandle.remove(handle.to_ptr.address)
|
|
1115
|
+
IncrementalAlterConfigsHandle.remove(handle.to_ptr.address) if registered
|
|
891
1116
|
|
|
892
1117
|
raise
|
|
893
1118
|
ensure
|
|
894
|
-
|
|
895
|
-
|
|
1119
|
+
if admin_options_ptr && !admin_options_ptr.null?
|
|
1120
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_destroy(admin_options_ptr)
|
|
1121
|
+
end
|
|
896
1122
|
|
|
897
|
-
if
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
1123
|
+
Rdkafka::Bindings.rd_kafka_queue_destroy(queue_ptr) if queue_ptr && !queue_ptr.null?
|
|
1124
|
+
|
|
1125
|
+
pointer_array.each do |config_resource_ptr|
|
|
1126
|
+
next if config_resource_ptr.null?
|
|
1127
|
+
|
|
1128
|
+
Rdkafka::Bindings.rd_kafka_ConfigResource_destroy(config_resource_ptr)
|
|
902
1129
|
end
|
|
903
1130
|
end
|
|
904
1131
|
|
data/lib/rdkafka/bindings.rb
CHANGED
|
@@ -88,7 +88,7 @@ module Rdkafka
|
|
|
88
88
|
|
|
89
89
|
# This function comes from our patch on top of librdkafka. It allows os to load all the
|
|
90
90
|
# librdkafka components without initializing the client
|
|
91
|
-
#
|
|
91
|
+
# See: https://github.com/confluentinc/librdkafka/issues/4590
|
|
92
92
|
attach_function :rd_kafka_global_init, [], :void
|
|
93
93
|
|
|
94
94
|
# Polling
|
|
@@ -170,6 +170,7 @@ module Rdkafka
|
|
|
170
170
|
|
|
171
171
|
attach_function :rd_kafka_DescribeConfigs, [:pointer, :pointer, :size_t, :pointer, :pointer], :void, blocking: true
|
|
172
172
|
attach_function :rd_kafka_ConfigResource_new, [:int32, :pointer], :pointer
|
|
173
|
+
attach_function :rd_kafka_ConfigResource_destroy, [:pointer], :void
|
|
173
174
|
attach_function :rd_kafka_ConfigResource_destroy_array, [:pointer, :int32], :void
|
|
174
175
|
attach_function :rd_kafka_event_DescribeConfigs_result, [:pointer], :pointer
|
|
175
176
|
attach_function :rd_kafka_DescribeConfigs_result_resources, [:pointer, :pointer], :pointer
|
|
@@ -271,10 +272,11 @@ module Rdkafka
|
|
|
271
272
|
|
|
272
273
|
# Queue IO Event Support - for fiber scheduler integration
|
|
273
274
|
# Enables notifications to a custom FD when queue transitions from empty to non-empty
|
|
274
|
-
#
|
|
275
|
-
#
|
|
276
|
-
#
|
|
277
|
-
#
|
|
275
|
+
# Arguments:
|
|
276
|
+
# - queue (rd_kafka_queue_t*) - the queue to monitor
|
|
277
|
+
# - fd (int) - file descriptor to write to (provide your own pipe/eventfd)
|
|
278
|
+
# - payload (const void*) - data to write to fd
|
|
279
|
+
# - size (size_t) - size of payload
|
|
278
280
|
attach_function :rd_kafka_queue_io_event_enable, [:pointer, :int, :pointer, :size_t], :void
|
|
279
281
|
# Per topic configs
|
|
280
282
|
attach_function :rd_kafka_topic_conf_new, [], :pointer
|
|
@@ -343,7 +345,8 @@ module Rdkafka
|
|
|
343
345
|
# across different parts of the codebase (callbacks, testing utilities, etc.).
|
|
344
346
|
#
|
|
345
347
|
# @param client_ptr [FFI::Pointer] Native kafka client pointer
|
|
346
|
-
# @return [Hash, nil] Hash with :error_code and :error_string if fatal error occurred,
|
|
348
|
+
# @return [Hash, nil] Hash with :error_code and :error_string if fatal error occurred,
|
|
349
|
+
# nil otherwise
|
|
347
350
|
#
|
|
348
351
|
# @example
|
|
349
352
|
# details = Rdkafka::Bindings.extract_fatal_error(client_ptr)
|
|
@@ -388,9 +391,10 @@ module Rdkafka
|
|
|
388
391
|
end
|
|
389
392
|
end
|
|
390
393
|
|
|
391
|
-
# The OAuth callback is currently global and contextless.
|
|
392
|
-
#
|
|
393
|
-
# The instance name will be provided in the callback, allowing the callback to
|
|
394
|
+
# The OAuth callback is currently global and contextless. This means that the callback will be
|
|
395
|
+
# called for all instances, and the callback must be able to determine to which instance it is
|
|
396
|
+
# associated. The instance name will be provided in the callback, allowing the callback to
|
|
397
|
+
# reference the correct instance.
|
|
394
398
|
#
|
|
395
399
|
# An example of how to use the instance name in the callback is given below.
|
|
396
400
|
# The `refresh_token` is configured as the `oauthbearer_token_refresh_callback`.
|
|
@@ -599,6 +603,37 @@ module Rdkafka
|
|
|
599
603
|
attach_function :rd_kafka_event_DeleteGroups_result, [:pointer], :pointer, blocking: true # rd_kafka_event_t* => rd_kafka_DeleteGroups_result_t*
|
|
600
604
|
attach_function :rd_kafka_DeleteGroups_result_groups, [:pointer, :pointer], :pointer, blocking: true # rd_kafka_DeleteGroups_result_t*, size_t* => rd_kafka_group_result_t**
|
|
601
605
|
|
|
606
|
+
# Delete Records
|
|
607
|
+
RD_KAFKA_ADMIN_OP_DELETERECORDS = 6 # rd_kafka_admin_op_t
|
|
608
|
+
RD_KAFKA_EVENT_DELETERECORDS_RESULT = 105 # rd_kafka_event_type_t
|
|
609
|
+
|
|
610
|
+
attach_function :rd_kafka_DeleteRecords, [:pointer, :pointer, :size_t, :pointer, :pointer], :void, blocking: true
|
|
611
|
+
attach_function :rd_kafka_DeleteRecords_new, [:pointer], :pointer, blocking: true
|
|
612
|
+
attach_function :rd_kafka_DeleteRecords_destroy, [:pointer], :void, blocking: true
|
|
613
|
+
attach_function :rd_kafka_event_DeleteRecords_result, [:pointer], :pointer, blocking: true # rd_kafka_event_t* => rd_kafka_DeleteRecords_result_t*
|
|
614
|
+
attach_function :rd_kafka_DeleteRecords_result_offsets, [:pointer], :pointer, blocking: true # rd_kafka_DeleteRecords_result_t* => rd_kafka_topic_partition_list_t*
|
|
615
|
+
|
|
616
|
+
# List Consumer Groups
|
|
617
|
+
RD_KAFKA_ADMIN_OP_LISTCONSUMERGROUPS = 12 # rd_kafka_admin_op_t
|
|
618
|
+
RD_KAFKA_EVENT_LISTCONSUMERGROUPS_RESULT = 0x2000 # rd_kafka_event_type_t
|
|
619
|
+
|
|
620
|
+
# Consumer group states (rd_kafka_consumer_group_state_t)
|
|
621
|
+
RD_KAFKA_CONSUMER_GROUP_STATE_UNKNOWN = 0
|
|
622
|
+
RD_KAFKA_CONSUMER_GROUP_STATE_PREPARING_REBALANCE = 1
|
|
623
|
+
RD_KAFKA_CONSUMER_GROUP_STATE_COMPLETING_REBALANCE = 2
|
|
624
|
+
RD_KAFKA_CONSUMER_GROUP_STATE_STABLE = 3
|
|
625
|
+
RD_KAFKA_CONSUMER_GROUP_STATE_DEAD = 4
|
|
626
|
+
RD_KAFKA_CONSUMER_GROUP_STATE_EMPTY = 5
|
|
627
|
+
|
|
628
|
+
attach_function :rd_kafka_ListConsumerGroups, [:pointer, :pointer, :pointer], :void, blocking: true
|
|
629
|
+
attach_function :rd_kafka_event_ListConsumerGroups_result, [:pointer], :pointer, blocking: true # rd_kafka_event_t* => rd_kafka_ListConsumerGroups_result_t*
|
|
630
|
+
attach_function :rd_kafka_ListConsumerGroups_result_valid, [:pointer, :pointer], :pointer, blocking: true # result*, size_t* => rd_kafka_ConsumerGroupListing_t**
|
|
631
|
+
attach_function :rd_kafka_ListConsumerGroups_result_errors, [:pointer, :pointer], :pointer, blocking: true # result*, size_t* => rd_kafka_error_t**
|
|
632
|
+
attach_function :rd_kafka_ConsumerGroupListing_group_id, [:pointer], :pointer, blocking: true # => const char*
|
|
633
|
+
attach_function :rd_kafka_ConsumerGroupListing_is_simple_consumer_group, [:pointer], :int, blocking: true
|
|
634
|
+
attach_function :rd_kafka_ConsumerGroupListing_state, [:pointer], :int, blocking: true
|
|
635
|
+
attach_function :rd_kafka_consumer_group_state_name, [:int], :pointer, blocking: true # => const char*
|
|
636
|
+
|
|
602
637
|
# Background Queue and Callback
|
|
603
638
|
|
|
604
639
|
attach_function :rd_kafka_conf_set_background_event_cb, [:pointer, :pointer], :void
|
|
@@ -661,21 +696,24 @@ module Rdkafka
|
|
|
661
696
|
attach_function :rd_kafka_AclBindingFilter_new, [:int32, :pointer, :int32, :pointer, :pointer, :int32, :int32, :pointer, :size_t], :pointer
|
|
662
697
|
attach_function :rd_kafka_AclBinding_destroy, [:pointer], :void
|
|
663
698
|
|
|
664
|
-
# rd_kafka_ResourceType_t -
|
|
699
|
+
# rd_kafka_ResourceType_t -
|
|
700
|
+
# https://github.com/confluentinc/librdkafka/blob/292d2a66b992/src/rdkafka.h#L7307
|
|
665
701
|
RD_KAFKA_RESOURCE_ANY = 1
|
|
666
702
|
RD_KAFKA_RESOURCE_TOPIC = 2
|
|
667
703
|
RD_KAFKA_RESOURCE_GROUP = 3
|
|
668
704
|
RD_KAFKA_RESOURCE_BROKER = 4
|
|
669
705
|
RD_KAFKA_RESOURCE_TRANSACTIONAL_ID = 5
|
|
670
706
|
|
|
671
|
-
# rd_kafka_ResourcePatternType_t -
|
|
707
|
+
# rd_kafka_ResourcePatternType_t -
|
|
708
|
+
# https://github.com/confluentinc/librdkafka/blob/292d2a66b992/src/rdkafka.h#L7320
|
|
672
709
|
RD_KAFKA_RESOURCE_PATTERN_UNKNOWN = 0
|
|
673
710
|
RD_KAFKA_RESOURCE_PATTERN_ANY = 1
|
|
674
711
|
RD_KAFKA_RESOURCE_PATTERN_MATCH = 2
|
|
675
712
|
RD_KAFKA_RESOURCE_PATTERN_LITERAL = 3
|
|
676
713
|
RD_KAFKA_RESOURCE_PATTERN_PREFIXED = 4
|
|
677
714
|
|
|
678
|
-
# rd_kafka_AclOperation_t -
|
|
715
|
+
# rd_kafka_AclOperation_t -
|
|
716
|
+
# https://github.com/confluentinc/librdkafka/blob/292d2a66b992/src/rdkafka.h#L8403
|
|
679
717
|
RD_KAFKA_ACL_OPERATION_ANY = 1
|
|
680
718
|
RD_KAFKA_ACL_OPERATION_ALL = 2
|
|
681
719
|
RD_KAFKA_ACL_OPERATION_READ = 3
|
|
@@ -689,7 +727,8 @@ module Rdkafka
|
|
|
689
727
|
RD_KAFKA_ACL_OPERATION_ALTER_CONFIGS = 11
|
|
690
728
|
RD_KAFKA_ACL_OPERATION_IDEMPOTENT_WRITE = 12
|
|
691
729
|
|
|
692
|
-
# rd_kafka_AclPermissionType_t -
|
|
730
|
+
# rd_kafka_AclPermissionType_t -
|
|
731
|
+
# https://github.com/confluentinc/librdkafka/blob/292d2a66b992/src/rdkafka.h#L8435
|
|
693
732
|
RD_KAFKA_ACL_PERMISSION_TYPE_ANY = 1
|
|
694
733
|
RD_KAFKA_ACL_PERMISSION_TYPE_DENY = 2
|
|
695
734
|
RD_KAFKA_ACL_PERMISSION_TYPE_ALLOW = 3
|