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.
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 result
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 creating the acl
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 deleting the acl
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 fetching acls
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
- queue_ptr = @native_kafka.with_inner do |inner|
733
- Rdkafka::Bindings.rd_kafka_queue_get_background(inner)
734
- end
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
- if queue_ptr.null?
737
- raise Rdkafka::Config::ConfigError.new("rd_kafka_queue_get_background was NULL")
738
- end
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
- admin_options_ptr = @native_kafka.with_inner do |inner|
741
- Rdkafka::Bindings.rd_kafka_AdminOptions_new(
742
- inner,
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
- DescribeConfigsHandle.register(handle)
748
- Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, handle.to_ptr)
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
- pointer_array = parsed_resources.map do |resource_type, resource_name|
751
- Rdkafka::Bindings.rd_kafka_ConfigResource_new(
752
- resource_type,
753
- FFI::MemoryPointer.from_string(resource_name)
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
- configs_array_ptr = FFI::MemoryPointer.new(:pointer, pointer_array.size)
758
- configs_array_ptr.write_array_of_pointer(pointer_array)
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
- Rdkafka::Bindings.rd_kafka_AdminOptions_destroy(admin_options_ptr)
776
- Rdkafka::Bindings.rd_kafka_queue_destroy(queue_ptr)
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 configs_array_ptr
779
- Rdkafka::Bindings.rd_kafka_ConfigResource_destroy_array(
780
- configs_array_ptr,
781
- pointer_array.size
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
- queue_ptr = @native_kafka.with_inner do |inner|
822
- Rdkafka::Bindings.rd_kafka_queue_get_background(inner)
823
- end
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
- if queue_ptr.null?
826
- raise Rdkafka::Config::ConfigError.new("rd_kafka_queue_get_background was NULL")
827
- end
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
- admin_options_ptr = @native_kafka.with_inner do |inner|
830
- Rdkafka::Bindings.rd_kafka_AdminOptions_new(
831
- inner,
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
- IncrementalAlterConfigsHandle.register(handle)
837
- Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, handle.to_ptr)
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
- add_error = nil
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
- pointer_array = parsed_resources.map do |resource_type, resource_name, configs|
842
- # First build the appropriate resource representation
843
- resource_ptr = Rdkafka::Bindings.rd_kafka_ConfigResource_new(
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
- configs.each do |name, op_type, value|
849
- # rd_kafka_ConfigResource_add_incremental_config returns a non-NULL rd_kafka_error_t for
850
- # an invalid op_type, an empty/nil name, or a nil value on a non-delete op. The result
851
- # used to be ignored: the entry was silently dropped, the alter request still reported
852
- # success, and the error object leaked. Capture the first error (build_from_c also
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
- error = Rdkafka::RdkafkaError.build_from_c(
862
- error_ptr,
863
- "rd_kafka_ConfigResource_add_incremental_config"
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
- add_error ||= error if error
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
- resource_ptr
870
- end
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
- Rdkafka::Bindings.rd_kafka_AdminOptions_destroy(admin_options_ptr)
895
- Rdkafka::Bindings.rd_kafka_queue_destroy(queue_ptr)
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 configs_array_ptr
898
- Rdkafka::Bindings.rd_kafka_ConfigResource_destroy_array(
899
- configs_array_ptr,
900
- pointer_array.size
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
 
@@ -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
- # @see https://github.com/confluentinc/librdkafka/issues/4590
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
- # @param queue rd_kafka_queue_t* - the queue to monitor
275
- # @param fd int - file descriptor to write to (provide your own pipe/eventfd)
276
- # @param payload const void* - data to write to fd
277
- # @param size size_t - size of payload
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, nil otherwise
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
- # This means that the callback will be called for all instances, and the callback must be able to determine to which instance it is associated.
393
- # The instance name will be provided in the callback, allowing the callback to reference the correct instance.
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 - https://github.com/confluentinc/librdkafka/blob/292d2a66b9921b783f08147807992e603c7af059/src/rdkafka.h#L7307
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 - https://github.com/confluentinc/librdkafka/blob/292d2a66b9921b783f08147807992e603c7af059/src/rdkafka.h#L7320
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 - https://github.com/confluentinc/librdkafka/blob/292d2a66b9921b783f08147807992e603c7af059/src/rdkafka.h#L8403
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 - https://github.com/confluentinc/librdkafka/blob/292d2a66b9921b783f08147807992e603c7af059/src/rdkafka.h#L8435
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