rdkafka 0.29.2-aarch64-linux-gnu → 0.31.0-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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -15
  3. data/README.md +2 -0
  4. data/docker-compose-ssl.yml +1 -0
  5. data/docker-compose.yml +1 -0
  6. data/ext/librdkafka.so +0 -0
  7. data/lib/rdkafka/admin/alter_consumer_group_offsets_handle.rb +31 -0
  8. data/lib/rdkafka/admin/alter_consumer_group_offsets_report.rb +50 -0
  9. data/lib/rdkafka/admin/delete_consumer_group_offsets_handle.rb +31 -0
  10. data/lib/rdkafka/admin/delete_consumer_group_offsets_report.rb +50 -0
  11. data/lib/rdkafka/admin/describe_cluster_handle.rb +31 -0
  12. data/lib/rdkafka/admin/describe_cluster_report.rb +48 -0
  13. data/lib/rdkafka/admin/describe_consumer_groups_handle.rb +31 -0
  14. data/lib/rdkafka/admin/describe_consumer_groups_report.rb +151 -0
  15. data/lib/rdkafka/admin/describe_result_parsing.rb +47 -0
  16. data/lib/rdkafka/admin/describe_topics_handle.rb +31 -0
  17. data/lib/rdkafka/admin/describe_topics_report.rb +118 -0
  18. data/lib/rdkafka/admin.rb +365 -0
  19. data/lib/rdkafka/bindings/admin/acls.rb +85 -0
  20. data/lib/rdkafka/bindings/admin/configs.rb +37 -0
  21. data/lib/rdkafka/bindings/admin/list_offsets.rb +26 -0
  22. data/lib/rdkafka/bindings/admin.rb +197 -0
  23. data/lib/rdkafka/bindings/callbacks.rb +100 -0
  24. data/lib/rdkafka/bindings/configuration.rb +50 -0
  25. data/lib/rdkafka/bindings/consumer.rb +83 -0
  26. data/lib/rdkafka/bindings/errors.rb +15 -0
  27. data/lib/rdkafka/bindings/handle.rb +16 -0
  28. data/lib/rdkafka/bindings/messages.rb +28 -0
  29. data/lib/rdkafka/bindings/metadata.rb +14 -0
  30. data/lib/rdkafka/bindings/polling.rb +17 -0
  31. data/lib/rdkafka/bindings/producer.rb +77 -0
  32. data/lib/rdkafka/bindings/structs.rb +62 -0
  33. data/lib/rdkafka/bindings.rb +12 -612
  34. data/lib/rdkafka/callbacks/alter_consumer_group_offsets_handler.rb +65 -0
  35. data/lib/rdkafka/callbacks/delete_consumer_group_offsets_handler.rb +65 -0
  36. data/lib/rdkafka/callbacks/describe_cluster_handler.rb +36 -0
  37. data/lib/rdkafka/callbacks/describe_consumer_groups_handler.rb +36 -0
  38. data/lib/rdkafka/callbacks/describe_topics_handler.rb +36 -0
  39. data/lib/rdkafka/callbacks.rb +24 -17
  40. data/lib/rdkafka/clients.rb +13 -46
  41. data/lib/rdkafka/config.rb +34 -0
  42. data/lib/rdkafka/consumer/message.rb +6 -9
  43. data/lib/rdkafka/producer/delivery_handle.rb +8 -2
  44. data/lib/rdkafka/producer/delivery_report.rb +51 -1
  45. data/lib/rdkafka/version.rb +3 -3
  46. data/lib/rdkafka.rb +16 -0
  47. data/package-lock.json +9 -9
  48. data/rdkafka.gemspec +1 -1
  49. data/renovate.json +3 -0
  50. metadata +33 -3
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rdkafka
4
+ class Admin
5
+ # Handle for describe topics operation
6
+ class DescribeTopicsHandle < AbstractHandle
7
+ layout :pending, :bool,
8
+ :response, :int
9
+
10
+ # @return [String] the name of the operation
11
+ def operation_name
12
+ "describe topics"
13
+ end
14
+
15
+ # @return [DescribeTopicsReport] report prepared by the background event callback, with
16
+ # the described topics.
17
+ def create_result
18
+ prepared_result
19
+ end
20
+
21
+ # Raises an error if the operation failed
22
+ # @raise [RdkafkaError]
23
+ def raise_error
24
+ raise RdkafkaError.new(
25
+ self[:response],
26
+ broker_message: broker_message
27
+ )
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rdkafka
4
+ class Admin
5
+ # Report for describe topics operation result
6
+ class DescribeTopicsReport
7
+ include DescribeResultParsing
8
+
9
+ # Described topics, one per requested topic name. Each entry is a hash with:
10
+ # - `:name` [String] the topic name
11
+ # - `:topic_id` [String, nil] the topic id (KIP-516), base64 encoded, or `nil` when the
12
+ # topic could not be described
13
+ # - `:is_internal` [Boolean] whether the topic is internal (e.g. `__consumer_offsets`)
14
+ # - `:partitions` [Array<Hash>] each with `:partition` [Integer], `:leader` [Hash, nil] and
15
+ # `:replicas` / `:isr` [Array<Hash>]; nodes have `:id`, `:host`, `:port` and `:rack`
16
+ # - `:authorized_operations` [Array<Integer>, nil] `Bindings::RD_KAFKA_ACL_OPERATION_*`
17
+ # codes, `nil` unless requested with `include_authorized_operations: true`
18
+ # - `:error` [RdkafkaError, nil] per-topic error (e.g. `unknown_topic_or_part`), `nil`
19
+ # on success
20
+ # @return [Array<Hash>]
21
+ attr_reader :topics
22
+
23
+ # @param result_ptr [FFI::Pointer] pointer to the `rd_kafka_DescribeTopics_result_t`
24
+ def initialize(result_ptr)
25
+ @topics = []
26
+
27
+ return if result_ptr.null?
28
+
29
+ count_ptr = FFI::MemoryPointer.new(:size_t)
30
+ array_ptr = Bindings.rd_kafka_DescribeTopics_result_topics(result_ptr, count_ptr)
31
+
32
+ return if array_ptr.null?
33
+
34
+ array_ptr.read_array_of_pointer(count_ptr.read(:size_t)).each do |topic_ptr|
35
+ @topics << extract_topic(topic_ptr)
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ # @param topic_ptr [FFI::Pointer] pointer to a `rd_kafka_TopicDescription_t`
42
+ # @return [Hash]
43
+ def extract_topic(topic_ptr)
44
+ name_ptr = Bindings.rd_kafka_TopicDescription_name(topic_ptr)
45
+ operations_count_ptr = FFI::MemoryPointer.new(:size_t)
46
+
47
+ {
48
+ name: name_ptr.null? ? nil : name_ptr.read_string,
49
+ topic_id: extract_topic_id(topic_ptr),
50
+ is_internal: Bindings.rd_kafka_TopicDescription_is_internal(topic_ptr) != 0,
51
+ partitions: extract_partitions(topic_ptr),
52
+ authorized_operations: extract_authorized_operations(
53
+ Bindings.rd_kafka_TopicDescription_authorized_operations(topic_ptr, operations_count_ptr),
54
+ operations_count_ptr
55
+ ),
56
+ error: extract_error(topic_ptr)
57
+ }
58
+ end
59
+
60
+ # librdkafka reports the zero UUID for a topic it could not describe
61
+ # @param topic_ptr [FFI::Pointer] pointer to a `rd_kafka_TopicDescription_t`
62
+ # @return [String, nil]
63
+ def extract_topic_id(topic_ptr)
64
+ uuid_ptr = Bindings.rd_kafka_TopicDescription_topic_id(topic_ptr)
65
+
66
+ return nil if uuid_ptr.null?
67
+ return nil if Bindings.rd_kafka_Uuid_most_significant_bits(uuid_ptr).zero? &&
68
+ Bindings.rd_kafka_Uuid_least_significant_bits(uuid_ptr).zero?
69
+
70
+ string_ptr = Bindings.rd_kafka_Uuid_base64str(uuid_ptr)
71
+ string_ptr.null? ? nil : string_ptr.read_string
72
+ end
73
+
74
+ # @param topic_ptr [FFI::Pointer] pointer to a `rd_kafka_TopicDescription_t`
75
+ # @return [Array<Hash>]
76
+ def extract_partitions(topic_ptr)
77
+ count_ptr = FFI::MemoryPointer.new(:size_t)
78
+ array_ptr = Bindings.rd_kafka_TopicDescription_partitions(topic_ptr, count_ptr)
79
+
80
+ return [] if array_ptr.null?
81
+
82
+ array_ptr.read_array_of_pointer(count_ptr.read(:size_t)).map do |partition_ptr|
83
+ replicas_count_ptr = FFI::MemoryPointer.new(:size_t)
84
+ isr_count_ptr = FFI::MemoryPointer.new(:size_t)
85
+
86
+ {
87
+ partition: Bindings.rd_kafka_TopicPartitionInfo_partition(partition_ptr),
88
+ leader: extract_node(Bindings.rd_kafka_TopicPartitionInfo_leader(partition_ptr)),
89
+ replicas: extract_nodes(
90
+ Bindings.rd_kafka_TopicPartitionInfo_replicas(partition_ptr, replicas_count_ptr),
91
+ replicas_count_ptr
92
+ ),
93
+ isr: extract_nodes(
94
+ Bindings.rd_kafka_TopicPartitionInfo_isr(partition_ptr, isr_count_ptr),
95
+ isr_count_ptr
96
+ )
97
+ }
98
+ end
99
+ end
100
+
101
+ # The error is owned by the result, so it is copied and not destroyed here.
102
+ # @param topic_ptr [FFI::Pointer] pointer to a `rd_kafka_TopicDescription_t`
103
+ # @return [RdkafkaError, nil]
104
+ def extract_error(topic_ptr)
105
+ error_ptr = Bindings.rd_kafka_TopicDescription_error(topic_ptr)
106
+
107
+ return nil if error_ptr.null?
108
+
109
+ code = Bindings.rd_kafka_error_code(error_ptr)
110
+
111
+ return nil if code == Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
112
+
113
+ string_ptr = Bindings.rd_kafka_error_string(error_ptr)
114
+ RdkafkaError.new(code, broker_message: string_ptr.null? ? nil : string_ptr.read_string)
115
+ end
116
+ end
117
+ end
118
+ end
data/lib/rdkafka/admin.rb CHANGED
@@ -224,6 +224,78 @@ module Rdkafka
224
224
  create_topic_handle
225
225
  end
226
226
 
227
+ # Sets the committed offsets of a consumer group from outside the group.
228
+ #
229
+ # Kafka requires the group to be empty (no active members) for this to be accepted; when it
230
+ # is not, the broker rejects the request and the handle raises with `group_not_empty` rather
231
+ # than reporting success.
232
+ #
233
+ # @param group_id [String] the group whose offsets we want to set
234
+ # @param topic_partition_list [TopicPartitionList] partitions with the offsets to commit
235
+ # @return [AlterConsumerGroupOffsetsHandle] handle that can be used to wait for the result
236
+ # @raise [RdkafkaError] when altering the offsets fails
237
+ def alter_consumer_group_offsets(group_id, topic_partition_list)
238
+ closed_admin_check(__method__)
239
+
240
+ native_tpl = topic_partition_list.to_native_tpl
241
+
242
+ begin
243
+ alter_ptr = Rdkafka::Bindings.rd_kafka_AlterConsumerGroupOffsets_new(
244
+ FFI::MemoryPointer.from_string(group_id),
245
+ native_tpl
246
+ )
247
+ rescue Exception
248
+ Rdkafka::Bindings.rd_kafka_topic_partition_list_destroy(native_tpl)
249
+ raise
250
+ end
251
+
252
+ # librdkafka copies the list into the request object, so ours is no longer needed
253
+ Rdkafka::Bindings.rd_kafka_topic_partition_list_destroy(native_tpl)
254
+
255
+ enqueue_group_offsets_op(
256
+ request_ptr: alter_ptr,
257
+ handle: AlterConsumerGroupOffsetsHandle.new,
258
+ admin_op: Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_ALTERCONSUMERGROUPOFFSETS,
259
+ destroy: Rdkafka::Bindings.method(:rd_kafka_AlterConsumerGroupOffsets_destroy),
260
+ enqueue: Rdkafka::Bindings.method(:rd_kafka_AlterConsumerGroupOffsets)
261
+ )
262
+ end
263
+
264
+ # Deletes the committed offsets of specific partitions of a consumer group, leaving the
265
+ # group and its remaining offsets in place.
266
+ #
267
+ # This is the partition scoped counterpart of {#delete_group}, which removes the whole group.
268
+ #
269
+ # @param group_id [String] the group whose offsets we want to delete
270
+ # @param topic_partition_list [TopicPartitionList] partitions whose offsets to delete
271
+ # @return [DeleteConsumerGroupOffsetsHandle] handle that can be used to wait for the result
272
+ # @raise [RdkafkaError] when deleting the offsets fails
273
+ def delete_consumer_group_offsets(group_id, topic_partition_list)
274
+ closed_admin_check(__method__)
275
+
276
+ native_tpl = topic_partition_list.to_native_tpl
277
+
278
+ begin
279
+ delete_ptr = Rdkafka::Bindings.rd_kafka_DeleteConsumerGroupOffsets_new(
280
+ FFI::MemoryPointer.from_string(group_id),
281
+ native_tpl
282
+ )
283
+ rescue Exception
284
+ Rdkafka::Bindings.rd_kafka_topic_partition_list_destroy(native_tpl)
285
+ raise
286
+ end
287
+
288
+ Rdkafka::Bindings.rd_kafka_topic_partition_list_destroy(native_tpl)
289
+
290
+ enqueue_group_offsets_op(
291
+ request_ptr: delete_ptr,
292
+ handle: DeleteConsumerGroupOffsetsHandle.new,
293
+ admin_op: Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_DELETECONSUMERGROUPOFFSETS,
294
+ destroy: Rdkafka::Bindings.method(:rd_kafka_DeleteConsumerGroupOffsets_destroy),
295
+ enqueue: Rdkafka::Bindings.method(:rd_kafka_DeleteConsumerGroupOffsets)
296
+ )
297
+ end
298
+
227
299
  # Deletes a consumer group
228
300
  #
229
301
  # @param group_id [String] the group id to delete
@@ -448,6 +520,231 @@ module Rdkafka
448
520
  handle
449
521
  end
450
522
 
523
+ # Describes the cluster: its id, the current controller and the broker nodes (with racks).
524
+ #
525
+ # Unlike {#metadata}, this does not fetch any topic metadata.
526
+ #
527
+ # @param include_authorized_operations [Boolean] when true, also return the operations this
528
+ # client is authorized to perform on the cluster
529
+ # @return [DescribeClusterHandle] handle that can be used to wait for the result
530
+ # @raise [RdkafkaError] when describing the cluster fails
531
+ #
532
+ # @example Print the cluster id, the controller and every broker
533
+ # report = admin.describe_cluster.wait(max_wait_timeout_ms: 15_000)
534
+ #
535
+ # puts "cluster: #{report.cluster_id}, controller: #{report.controller[:id]}"
536
+ # report.nodes.each do |node|
537
+ # puts "#{node[:id]} #{node[:host]}:#{node[:port]} #{node[:rack]}"
538
+ # end
539
+ def describe_cluster(include_authorized_operations: false)
540
+ closed_admin_check(__method__)
541
+
542
+ queue_ptr = @native_kafka.with_inner do |inner|
543
+ Rdkafka::Bindings.rd_kafka_queue_get_background(inner)
544
+ end
545
+
546
+ if queue_ptr.null?
547
+ raise Rdkafka::Config::ConfigError.new("rd_kafka_queue_get_background was NULL")
548
+ end
549
+
550
+ handle = DescribeClusterHandle.new
551
+ handle[:pending] = true
552
+ handle[:response] = Rdkafka::Bindings::RD_KAFKA_PARTITION_UA
553
+ DescribeClusterHandle.register(handle)
554
+
555
+ admin_options_ptr = @native_kafka.with_inner do |inner|
556
+ Rdkafka::Bindings.rd_kafka_AdminOptions_new(
557
+ inner,
558
+ Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_DESCRIBECLUSTER
559
+ )
560
+ end
561
+ Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, handle.to_ptr)
562
+
563
+ begin
564
+ set_include_authorized_operations(admin_options_ptr) if include_authorized_operations
565
+
566
+ @native_kafka.with_inner do |inner|
567
+ Rdkafka::Bindings.rd_kafka_DescribeCluster(
568
+ inner,
569
+ admin_options_ptr,
570
+ queue_ptr
571
+ )
572
+ end
573
+ rescue Exception
574
+ DescribeClusterHandle.remove(handle.to_ptr.address)
575
+ raise
576
+ ensure
577
+ Rdkafka::Bindings.rd_kafka_AdminOptions_destroy(admin_options_ptr)
578
+ Rdkafka::Bindings.rd_kafka_queue_destroy(queue_ptr)
579
+ end
580
+
581
+ handle
582
+ end
583
+
584
+ # Describes the given topics: their topic ids, partitions (leader, replicas and in-sync
585
+ # replicas, with racks) and, optionally, the operations this client is authorized to perform.
586
+ #
587
+ # A topic that cannot be described (e.g. it does not exist) does not fail the whole request;
588
+ # its entry in the report carries an `:error` instead.
589
+ #
590
+ # @param topic_names [Array<String>] names of the topics to describe
591
+ # @param include_authorized_operations [Boolean] when true, also return the operations this
592
+ # client is authorized to perform on each topic
593
+ # @return [DescribeTopicsHandle] handle that can be used to wait for the result
594
+ # @raise [ArgumentError] when topic_names is not an Array of Strings
595
+ # @raise [RdkafkaError] when describing the topics fails
596
+ #
597
+ # @example Print the topic id and partition leaders of a topic
598
+ # report = admin.describe_topics(["events"]).wait(max_wait_timeout_ms: 15_000)
599
+ #
600
+ # report.topics.each do |topic|
601
+ # next warn("#{topic[:name]}: #{topic[:error].message}") if topic[:error]
602
+ #
603
+ # puts "#{topic[:name]} (#{topic[:topic_id]})"
604
+ # topic[:partitions].each do |partition|
605
+ # puts " #{partition[:partition]} -> #{partition[:leader]&.fetch(:id)}"
606
+ # end
607
+ # end
608
+ def describe_topics(topic_names, include_authorized_operations: false)
609
+ closed_admin_check(__method__)
610
+
611
+ unless topic_names.is_a?(Array) && topic_names.all?(String)
612
+ raise ArgumentError, "topic_names must be an Array of Strings"
613
+ end
614
+
615
+ queue_ptr = @native_kafka.with_inner do |inner|
616
+ Rdkafka::Bindings.rd_kafka_queue_get_background(inner)
617
+ end
618
+
619
+ if queue_ptr.null?
620
+ raise Rdkafka::Config::ConfigError.new("rd_kafka_queue_get_background was NULL")
621
+ end
622
+
623
+ handle = DescribeTopicsHandle.new
624
+ handle[:pending] = true
625
+ handle[:response] = Rdkafka::Bindings::RD_KAFKA_PARTITION_UA
626
+ DescribeTopicsHandle.register(handle)
627
+
628
+ admin_options_ptr = @native_kafka.with_inner do |inner|
629
+ Rdkafka::Bindings.rd_kafka_AdminOptions_new(
630
+ inner,
631
+ Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_DESCRIBETOPICS
632
+ )
633
+ end
634
+ Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, handle.to_ptr)
635
+
636
+ topic_collection_ptr = nil
637
+
638
+ begin
639
+ set_include_authorized_operations(admin_options_ptr) if include_authorized_operations
640
+
641
+ # librdkafka copies the names, so the Ruby-owned strings only need to outlive this call
642
+ name_ptrs = topic_names.map { |name| FFI::MemoryPointer.from_string(name) }
643
+ names_array_ptr = FFI::MemoryPointer.new(:pointer, [name_ptrs.size, 1].max)
644
+ names_array_ptr.write_array_of_pointer(name_ptrs)
645
+
646
+ topic_collection_ptr = Rdkafka::Bindings.rd_kafka_TopicCollection_of_topic_names(
647
+ names_array_ptr,
648
+ name_ptrs.size
649
+ )
650
+
651
+ @native_kafka.with_inner do |inner|
652
+ Rdkafka::Bindings.rd_kafka_DescribeTopics(
653
+ inner,
654
+ topic_collection_ptr,
655
+ admin_options_ptr,
656
+ queue_ptr
657
+ )
658
+ end
659
+ rescue Exception
660
+ DescribeTopicsHandle.remove(handle.to_ptr.address)
661
+ raise
662
+ ensure
663
+ Rdkafka::Bindings.rd_kafka_TopicCollection_destroy(topic_collection_ptr) if topic_collection_ptr
664
+ Rdkafka::Bindings.rd_kafka_AdminOptions_destroy(admin_options_ptr)
665
+ Rdkafka::Bindings.rd_kafka_queue_destroy(queue_ptr)
666
+ end
667
+
668
+ handle
669
+ end
670
+
671
+ # Describes the given consumer groups: their state, partition assignor, coordinator and
672
+ # members with their partition assignments.
673
+ #
674
+ # Each requested group is reported separately, with its own `:error`. A group id the cluster
675
+ # does not know is not an error: it is reported in the `Dead` state with no members.
676
+ #
677
+ # @param group_ids [Array<String>] ids of the consumer groups to describe
678
+ # @param include_authorized_operations [Boolean] whether to also fetch the ACL operations the
679
+ # client is authorized to perform on each group (reported as `:authorized_operations`)
680
+ # @return [DescribeConsumerGroupsHandle] handle that can be used to wait for the result
681
+ # @raise [RdkafkaError] when describing the consumer groups fails as a whole
682
+ #
683
+ # @example Describe a consumer group and print its members and their assignments
684
+ # report = admin.describe_consumer_groups(["example-group"]).wait(max_wait_timeout_ms: 15_000)
685
+ #
686
+ # report.groups.each do |group|
687
+ # next warn "#{group[:group_id]}: #{group[:error].message}" if group[:error]
688
+ #
689
+ # puts "#{group[:group_id]} - #{group[:state_name]} (#{group[:members].size} members)"
690
+ #
691
+ # group[:members].each do |member|
692
+ # puts " #{member[:client_id]}@#{member[:host]}: #{member[:assignment]}"
693
+ # end
694
+ # end
695
+ def describe_consumer_groups(group_ids, include_authorized_operations: false)
696
+ closed_admin_check(__method__)
697
+
698
+ group_id_ptrs = group_ids.map { |group_id| FFI::MemoryPointer.from_string(group_id) }
699
+ group_ids_ptr = FFI::MemoryPointer.new(:pointer, group_id_ptrs.size)
700
+ group_ids_ptr.write_array_of_pointer(group_id_ptrs)
701
+
702
+ # Get a pointer to the queue that our request will be enqueued on
703
+ queue_ptr = @native_kafka.with_inner do |inner|
704
+ Rdkafka::Bindings.rd_kafka_queue_get_background(inner)
705
+ end
706
+
707
+ if queue_ptr.null?
708
+ raise Rdkafka::Config::ConfigError.new("rd_kafka_queue_get_background was NULL")
709
+ end
710
+
711
+ # Create and register the handle we will return to the caller
712
+ handle = DescribeConsumerGroupsHandle.new
713
+ handle[:pending] = true
714
+ handle[:response] = Rdkafka::Bindings::RD_KAFKA_PARTITION_UA
715
+ DescribeConsumerGroupsHandle.register(handle)
716
+
717
+ admin_options_ptr = @native_kafka.with_inner do |inner|
718
+ Rdkafka::Bindings.rd_kafka_AdminOptions_new(
719
+ inner,
720
+ Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_DESCRIBECONSUMERGROUPS
721
+ )
722
+ end
723
+ Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, handle.to_ptr)
724
+
725
+ begin
726
+ set_include_authorized_operations(admin_options_ptr) if include_authorized_operations
727
+
728
+ @native_kafka.with_inner do |inner|
729
+ Rdkafka::Bindings.rd_kafka_DescribeConsumerGroups(
730
+ inner,
731
+ group_ids_ptr,
732
+ group_id_ptrs.size,
733
+ admin_options_ptr,
734
+ queue_ptr
735
+ )
736
+ end
737
+ rescue Exception
738
+ DescribeConsumerGroupsHandle.remove(handle.to_ptr.address)
739
+ raise
740
+ ensure
741
+ Rdkafka::Bindings.rd_kafka_AdminOptions_destroy(admin_options_ptr)
742
+ Rdkafka::Bindings.rd_kafka_queue_destroy(queue_ptr)
743
+ end
744
+
745
+ handle
746
+ end
747
+
451
748
  # Deletes the named topic
452
749
  #
453
750
  # @param topic_name [String] name of the topic to delete
@@ -1139,6 +1436,74 @@ module Rdkafka
1139
1436
 
1140
1437
  private
1141
1438
 
1439
+ # Shared enqueue path for the two consumer group offset admin operations. They differ only
1440
+ # in their C entry points, so keeping one body avoids two near identical copies of the
1441
+ # queue/handle/options dance and its cleanup.
1442
+ #
1443
+ # @param request_ptr [FFI::Pointer] the op specific request object
1444
+ # @param handle [AbstractHandle] the handle to register and return
1445
+ # @param admin_op [Integer] the RD_KAFKA_ADMIN_OP_* constant for this operation
1446
+ # @param destroy [Method] bound function used to free the request object
1447
+ # @param enqueue [Method] bound function used to enqueue the request
1448
+ # @return [AbstractHandle] the registered handle
1449
+ def enqueue_group_offsets_op(request_ptr:, handle:, admin_op:, destroy:, enqueue:)
1450
+ array_ptr = FFI::MemoryPointer.new(:pointer)
1451
+ array_ptr.write_array_of_pointer([request_ptr])
1452
+
1453
+ queue_ptr = @native_kafka.with_inner do |inner|
1454
+ Rdkafka::Bindings.rd_kafka_queue_get_background(inner)
1455
+ end
1456
+
1457
+ if queue_ptr.null?
1458
+ destroy.call(request_ptr)
1459
+ raise Rdkafka::Config::ConfigError.new("rd_kafka_queue_get_background was NULL")
1460
+ end
1461
+
1462
+ handle[:pending] = true
1463
+ handle[:response] = Rdkafka::Bindings::RD_KAFKA_PARTITION_UA
1464
+ handle.class.register(handle)
1465
+
1466
+ admin_options_ptr = @native_kafka.with_inner do |inner|
1467
+ Rdkafka::Bindings.rd_kafka_AdminOptions_new(inner, admin_op)
1468
+ end
1469
+ Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, handle.to_ptr)
1470
+
1471
+ begin
1472
+ @native_kafka.with_inner do |inner|
1473
+ enqueue.call(inner, array_ptr, 1, admin_options_ptr, queue_ptr)
1474
+ end
1475
+ rescue Exception
1476
+ handle.class.remove(handle.to_ptr.address)
1477
+ raise
1478
+ ensure
1479
+ Rdkafka::Bindings.rd_kafka_AdminOptions_destroy(admin_options_ptr)
1480
+ Rdkafka::Bindings.rd_kafka_queue_destroy(queue_ptr)
1481
+ destroy.call(request_ptr)
1482
+ end
1483
+
1484
+ handle
1485
+ end
1486
+
1487
+ # Asks librdkafka to include authorized operations in a describe result
1488
+ # @param admin_options_ptr [FFI::Pointer] the admin options to update
1489
+ # @raise [RdkafkaError] when librdkafka rejects the option
1490
+ def set_include_authorized_operations(admin_options_ptr)
1491
+ error_ptr = Rdkafka::Bindings.rd_kafka_AdminOptions_set_include_authorized_operations(admin_options_ptr, 1)
1492
+
1493
+ return if error_ptr.null?
1494
+
1495
+ begin
1496
+ string_ptr = Rdkafka::Bindings.rd_kafka_error_string(error_ptr)
1497
+
1498
+ raise RdkafkaError.new(
1499
+ Rdkafka::Bindings.rd_kafka_error_code(error_ptr),
1500
+ broker_message: string_ptr.null? ? nil : string_ptr.read_string
1501
+ )
1502
+ ensure
1503
+ Rdkafka::Bindings.rd_kafka_error_destroy(error_ptr)
1504
+ end
1505
+ end
1506
+
1142
1507
  # Checks if the admin is closed and raises an error if so
1143
1508
  # @param method [Symbol] name of the calling method for error context
1144
1509
  # @raise [ClosedAdminError] when the admin is closed
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rdkafka
4
+ module Bindings
5
+ # Create Acls
6
+ RD_KAFKA_ADMIN_OP_CREATEACLS = 9
7
+ RD_KAFKA_EVENT_CREATEACLS_RESULT = 1024
8
+
9
+ attach_function :rd_kafka_CreateAcls, [:pointer, :pointer, :size_t, :pointer, :pointer], :void
10
+ attach_function :rd_kafka_event_CreateAcls_result, [:pointer], :pointer
11
+ attach_function :rd_kafka_CreateAcls_result_acls, [:pointer, :pointer], :pointer
12
+
13
+ # Delete Acls
14
+ RD_KAFKA_ADMIN_OP_DELETEACLS = 11
15
+ RD_KAFKA_EVENT_DELETEACLS_RESULT = 4096
16
+
17
+ attach_function :rd_kafka_DeleteAcls, [:pointer, :pointer, :size_t, :pointer, :pointer], :void
18
+ attach_function :rd_kafka_event_DeleteAcls_result, [:pointer], :pointer
19
+ attach_function :rd_kafka_DeleteAcls_result_responses, [:pointer, :pointer], :pointer
20
+ attach_function :rd_kafka_DeleteAcls_result_response_error, [:pointer], :pointer
21
+ attach_function :rd_kafka_DeleteAcls_result_response_matching_acls, [:pointer, :pointer], :pointer
22
+
23
+ # Describe Acls
24
+ RD_KAFKA_ADMIN_OP_DESCRIBEACLS = 10
25
+ RD_KAFKA_EVENT_DESCRIBEACLS_RESULT = 2048
26
+
27
+ attach_function :rd_kafka_DescribeAcls, [:pointer, :pointer, :pointer, :pointer], :void
28
+ attach_function :rd_kafka_event_DescribeAcls_result, [:pointer], :pointer
29
+ attach_function :rd_kafka_DescribeAcls_result_acls, [:pointer, :pointer], :pointer
30
+
31
+ # Acl Bindings
32
+
33
+ attach_function :rd_kafka_AclBinding_restype, [:pointer], :int32
34
+ attach_function :rd_kafka_AclBinding_name, [:pointer], :pointer
35
+ attach_function :rd_kafka_AclBinding_resource_pattern_type, [:pointer], :int32
36
+ attach_function :rd_kafka_AclBinding_principal, [:pointer], :pointer
37
+ attach_function :rd_kafka_AclBinding_host, [:pointer], :pointer
38
+ attach_function :rd_kafka_AclBinding_operation, [:pointer], :int32
39
+ attach_function :rd_kafka_AclBinding_permission_type, [:pointer], :int32
40
+ attach_function :rd_kafka_AclBinding_new, [:int32, :pointer, :int32, :pointer, :pointer, :int32, :int32, :pointer, :size_t], :pointer
41
+ attach_function :rd_kafka_AclBindingFilter_new, [:int32, :pointer, :int32, :pointer, :pointer, :int32, :int32, :pointer, :size_t], :pointer
42
+ attach_function :rd_kafka_AclBinding_destroy, [:pointer], :void
43
+
44
+ # rd_kafka_ResourceType_t -
45
+ # https://github.com/confluentinc/librdkafka/blob/292d2a66b992/src/rdkafka.h#L7307
46
+ RD_KAFKA_RESOURCE_ANY = 1
47
+ RD_KAFKA_RESOURCE_TOPIC = 2
48
+ RD_KAFKA_RESOURCE_GROUP = 3
49
+ RD_KAFKA_RESOURCE_BROKER = 4
50
+ RD_KAFKA_RESOURCE_TRANSACTIONAL_ID = 5
51
+
52
+ # rd_kafka_ResourcePatternType_t -
53
+ # https://github.com/confluentinc/librdkafka/blob/292d2a66b992/src/rdkafka.h#L7320
54
+ RD_KAFKA_RESOURCE_PATTERN_UNKNOWN = 0
55
+ RD_KAFKA_RESOURCE_PATTERN_ANY = 1
56
+ RD_KAFKA_RESOURCE_PATTERN_MATCH = 2
57
+ RD_KAFKA_RESOURCE_PATTERN_LITERAL = 3
58
+ RD_KAFKA_RESOURCE_PATTERN_PREFIXED = 4
59
+
60
+ # rd_kafka_AclOperation_t -
61
+ # https://github.com/confluentinc/librdkafka/blob/292d2a66b992/src/rdkafka.h#L8403
62
+ RD_KAFKA_ACL_OPERATION_ANY = 1
63
+ RD_KAFKA_ACL_OPERATION_ALL = 2
64
+ RD_KAFKA_ACL_OPERATION_READ = 3
65
+ RD_KAFKA_ACL_OPERATION_WRITE = 4
66
+ RD_KAFKA_ACL_OPERATION_CREATE = 5
67
+ RD_KAFKA_ACL_OPERATION_DELETE = 6
68
+ RD_KAFKA_ACL_OPERATION_ALTER = 7
69
+ RD_KAFKA_ACL_OPERATION_DESCRIBE = 8
70
+ RD_KAFKA_ACL_OPERATION_CLUSTER_ACTION = 9
71
+ RD_KAFKA_ACL_OPERATION_DESCRIBE_CONFIGS = 10
72
+ RD_KAFKA_ACL_OPERATION_ALTER_CONFIGS = 11
73
+ RD_KAFKA_ACL_OPERATION_IDEMPOTENT_WRITE = 12
74
+
75
+ # rd_kafka_AclPermissionType_t -
76
+ # https://github.com/confluentinc/librdkafka/blob/292d2a66b992/src/rdkafka.h#L8435
77
+ RD_KAFKA_ACL_PERMISSION_TYPE_ANY = 1
78
+ RD_KAFKA_ACL_PERMISSION_TYPE_DENY = 2
79
+ RD_KAFKA_ACL_PERMISSION_TYPE_ALLOW = 3
80
+
81
+ # Extracting error details from Acl results
82
+ attach_function :rd_kafka_acl_result_error, [:pointer], :pointer
83
+ attach_function :rd_kafka_AclBinding_error, [:pointer], :pointer
84
+ end
85
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rdkafka
4
+ module Bindings
5
+ attach_function :rd_kafka_DescribeConfigs, [:pointer, :pointer, :size_t, :pointer, :pointer], :void, blocking: true
6
+ attach_function :rd_kafka_ConfigResource_new, [:int32, :pointer], :pointer
7
+ attach_function :rd_kafka_ConfigResource_destroy, [:pointer], :void
8
+ attach_function :rd_kafka_ConfigResource_destroy_array, [:pointer, :int32], :void
9
+ attach_function :rd_kafka_event_DescribeConfigs_result, [:pointer], :pointer
10
+ attach_function :rd_kafka_DescribeConfigs_result_resources, [:pointer, :pointer], :pointer
11
+ attach_function :rd_kafka_ConfigResource_configs, [:pointer, :pointer], :pointer
12
+ attach_function :rd_kafka_ConfigEntry_name, [:pointer], :string
13
+ attach_function :rd_kafka_ConfigEntry_value, [:pointer], :string
14
+ attach_function :rd_kafka_ConfigEntry_is_read_only, [:pointer], :int
15
+ attach_function :rd_kafka_ConfigEntry_is_default, [:pointer], :int
16
+ attach_function :rd_kafka_ConfigEntry_is_sensitive, [:pointer], :int
17
+ attach_function :rd_kafka_ConfigEntry_is_synonym, [:pointer], :int
18
+ attach_function :rd_kafka_ConfigEntry_synonyms, [:pointer, :pointer], :pointer
19
+ attach_function :rd_kafka_ConfigResource_error, [:pointer], :int
20
+ attach_function :rd_kafka_ConfigResource_error_string, [:pointer], :string
21
+ attach_function :rd_kafka_IncrementalAlterConfigs, [:pointer, :pointer, :size_t, :pointer, :pointer], :void, blocking: true
22
+ attach_function :rd_kafka_IncrementalAlterConfigs_result_resources, [:pointer, :pointer], :pointer
23
+ attach_function :rd_kafka_ConfigResource_add_incremental_config, [:pointer, :string, :int32, :string], :pointer
24
+ attach_function :rd_kafka_event_IncrementalAlterConfigs_result, [:pointer], :pointer
25
+
26
+ RD_KAFKA_ADMIN_OP_DESCRIBECONFIGS = 5
27
+ RD_KAFKA_EVENT_DESCRIBECONFIGS_RESULT = 104
28
+
29
+ RD_KAFKA_ADMIN_OP_INCREMENTALALTERCONFIGS = 16
30
+ RD_KAFKA_EVENT_INCREMENTALALTERCONFIGS_RESULT = 131072
31
+
32
+ RD_KAFKA_ALTER_CONFIG_OP_TYPE_SET = 0
33
+ RD_KAFKA_ALTER_CONFIG_OP_TYPE_DELETE = 1
34
+ RD_KAFKA_ALTER_CONFIG_OP_TYPE_APPEND = 2
35
+ RD_KAFKA_ALTER_CONFIG_OP_TYPE_SUBTRACT = 3
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rdkafka
4
+ module Bindings
5
+ # List Offsets
6
+ RD_KAFKA_ADMIN_OP_LISTOFFSETS = 21
7
+ RD_KAFKA_EVENT_LISTOFFSETS_RESULT = 0x400000
8
+
9
+ # rd_kafka_IsolationLevel_t
10
+ RD_KAFKA_ISOLATION_LEVEL_READ_UNCOMMITTED = 0
11
+ RD_KAFKA_ISOLATION_LEVEL_READ_COMMITTED = 1
12
+
13
+ # rd_kafka_OffsetSpec_t
14
+ RD_KAFKA_OFFSET_SPEC_MAX_TIMESTAMP = -3
15
+ RD_KAFKA_OFFSET_SPEC_EARLIEST = -2
16
+ RD_KAFKA_OFFSET_SPEC_LATEST = -1
17
+
18
+ attach_function :rd_kafka_ListOffsets, [:pointer, :pointer, :pointer, :pointer], :void, blocking: true
19
+ attach_function :rd_kafka_event_ListOffsets_result, [:pointer], :pointer
20
+ attach_function :rd_kafka_ListOffsets_result_infos, [:pointer, :pointer], :pointer
21
+ attach_function :rd_kafka_ListOffsetsResultInfo_topic_partition, [:pointer], :pointer
22
+ attach_function :rd_kafka_ListOffsetsResultInfo_timestamp, [:pointer], :int64
23
+ attach_function :rd_kafka_AdminOptions_set_isolation_level, [:pointer, :int], :pointer
24
+ attach_function :rd_kafka_topic_partition_get_leader_epoch, [:pointer], :int32
25
+ end
26
+ end