karafka-rdkafka 0.30.2-aarch64-linux-gnu → 0.32.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +100 -36
  3. data/README.md +3 -1
  4. data/docker-compose-ssl.yml +1 -0
  5. data/docker-compose.yml +1 -0
  6. data/ext/librdkafka.so +0 -0
  7. data/karafka-rdkafka.gemspec +5 -4
  8. data/lib/rdkafka/admin/acl_binding_result.rb +4 -16
  9. data/lib/rdkafka/admin/alter_consumer_group_offsets_report.rb +2 -2
  10. data/lib/rdkafka/admin/create_acl_report.rb +1 -3
  11. data/lib/rdkafka/admin/create_topic_report.rb +2 -6
  12. data/lib/rdkafka/admin/delete_consumer_group_offsets_report.rb +2 -2
  13. data/lib/rdkafka/admin/delete_groups_report.rb +2 -6
  14. data/lib/rdkafka/admin/delete_topic_report.rb +2 -6
  15. data/lib/rdkafka/admin/describe_cluster_handle.rb +31 -0
  16. data/lib/rdkafka/admin/describe_cluster_report.rb +51 -0
  17. data/lib/rdkafka/admin/describe_consumer_groups_handle.rb +31 -0
  18. data/lib/rdkafka/admin/describe_consumer_groups_report.rb +151 -0
  19. data/lib/rdkafka/admin/describe_result_parsing.rb +47 -0
  20. data/lib/rdkafka/admin/describe_topics_handle.rb +31 -0
  21. data/lib/rdkafka/admin/describe_topics_report.rb +118 -0
  22. data/lib/rdkafka/admin/list_consumer_group_offsets_handle.rb +31 -0
  23. data/lib/rdkafka/admin/list_consumer_group_offsets_report.rb +52 -0
  24. data/lib/rdkafka/admin.rb +424 -9
  25. data/lib/rdkafka/bindings/admin/acls.rb +85 -0
  26. data/lib/rdkafka/bindings/admin/configs.rb +37 -0
  27. data/lib/rdkafka/bindings/admin/list_offsets.rb +26 -0
  28. data/lib/rdkafka/bindings/admin.rb +211 -0
  29. data/lib/rdkafka/bindings/callbacks.rb +155 -0
  30. data/lib/rdkafka/bindings/configuration.rb +50 -0
  31. data/lib/rdkafka/bindings/consumer.rb +125 -0
  32. data/lib/rdkafka/bindings/errors.rb +17 -0
  33. data/lib/rdkafka/bindings/handle.rb +16 -0
  34. data/lib/rdkafka/bindings/messages.rb +28 -0
  35. data/lib/rdkafka/bindings/metadata.rb +17 -0
  36. data/lib/rdkafka/bindings/polling.rb +17 -0
  37. data/lib/rdkafka/bindings/producer.rb +82 -0
  38. data/lib/rdkafka/bindings/share_consumer.rb +67 -0
  39. data/lib/rdkafka/bindings/structs.rb +62 -0
  40. data/lib/rdkafka/bindings.rb +16 -775
  41. data/lib/rdkafka/callbacks/describe_cluster_handler.rb +36 -0
  42. data/lib/rdkafka/callbacks/describe_consumer_groups_handler.rb +36 -0
  43. data/lib/rdkafka/callbacks/describe_topics_handler.rb +36 -0
  44. data/lib/rdkafka/callbacks/list_consumer_group_offsets_handler.rb +62 -0
  45. data/lib/rdkafka/callbacks.rb +15 -7
  46. data/lib/rdkafka/clients.rb +13 -46
  47. data/lib/rdkafka/config.rb +70 -0
  48. data/lib/rdkafka/consumer/allocator.rb +0 -2
  49. data/lib/rdkafka/consumer/message.rb +6 -9
  50. data/lib/rdkafka/consumer/topic_partition_list.rb +7 -7
  51. data/lib/rdkafka/consumer.rb +76 -36
  52. data/lib/rdkafka/defaults.rb +6 -4
  53. data/lib/rdkafka/native_kafka.rb +45 -31
  54. data/lib/rdkafka/producer/delivery_handle.rb +18 -16
  55. data/lib/rdkafka/producer/delivery_report.rb +51 -1
  56. data/lib/rdkafka/producer/partitions_count_cache.rb +109 -26
  57. data/lib/rdkafka/producer.rb +141 -71
  58. data/lib/rdkafka/share_consumer.rb +70 -34
  59. data/lib/rdkafka/version.rb +3 -3
  60. data/lib/rdkafka.rb +14 -1
  61. data/package-lock.json +9 -9
  62. data/renovate.json +7 -0
  63. metadata +31 -31
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rdkafka
4
+ class Admin
5
+ # Report for describe consumer groups operation result
6
+ class DescribeConsumerGroupsReport
7
+ include DescribeResultParsing
8
+
9
+ # Described consumer groups, one per requested group id. Each entry is a hash with:
10
+ # - `:group_id` [String] the consumer group id
11
+ # - `:error` [RdkafkaError, nil] error describing this group, or `nil` on success. The
12
+ # other fields are not meaningful when it is set.
13
+ # - `:is_simple_consumer_group` [Boolean] `true` for a group that assigns partitions
14
+ # manually and uses Kafka only for offset storage
15
+ # - `:state` [Integer] the group state as a `Bindings::RD_KAFKA_CONSUMER_GROUP_STATE_*`
16
+ # code. A group id the cluster does not know is reported as
17
+ # `RD_KAFKA_CONSUMER_GROUP_STATE_DEAD` with no members.
18
+ # - `:state_name` [String] human-readable name of that state (e.g. `"Stable"`, `"Empty"`)
19
+ # - `:type` [Integer] the group protocol type as a
20
+ # `Bindings::RD_KAFKA_CONSUMER_GROUP_TYPE_*` code, one of:
21
+ # `RD_KAFKA_CONSUMER_GROUP_TYPE_CLASSIC`,
22
+ # `RD_KAFKA_CONSUMER_GROUP_TYPE_CONSUMER` (KIP-848),
23
+ # `RD_KAFKA_CONSUMER_GROUP_TYPE_UNKNOWN`
24
+ # - `:type_name` [String] human-readable name of that type (e.g. `"Classic"`, `"Consumer"`)
25
+ # - `:partition_assignor` [String, nil] partition assignor in use (e.g. `"range"`)
26
+ # - `:coordinator` [Hash, nil] the group coordinator broker as
27
+ # `{ id:, host:, port:, rack: }`
28
+ # - `:authorized_operations` [Array<Integer>, nil] ACL operations the client may perform on
29
+ # the group, as `Bindings::RD_KAFKA_ACL_OPERATION_*` codes. `nil` unless requested with
30
+ # `include_authorized_operations: true`.
31
+ # - `:members` [Array<Hash>] current group members, each with:
32
+ # - `:member_id` [String] the member (consumer) id
33
+ # - `:client_id` [String] the member's `client.id`
34
+ # - `:group_instance_id` [String, nil] the static membership instance id, if any
35
+ # - `:host` [String] the host the member connects from
36
+ # - `:assignment` [Hash{String => Array<Integer>}] assigned partitions, by topic
37
+ # - `:target_assignment` [Hash{String => Array<Integer>}, nil] partitions the member is
38
+ # moving to during a rebalance, by topic. Only `consumer` (KIP-848) groups have it;
39
+ # `nil` for `classic` groups.
40
+ # @return [Array<Hash>]
41
+ attr_reader :groups
42
+
43
+ # @param result_ptr [FFI::Pointer] pointer to the `rd_kafka_DescribeConsumerGroups_result_t`
44
+ def initialize(result_ptr)
45
+ @groups = []
46
+
47
+ return if result_ptr.null?
48
+
49
+ count_ptr = FFI::MemoryPointer.new(:size_t)
50
+ array_ptr = Bindings.rd_kafka_DescribeConsumerGroups_result_groups(result_ptr, count_ptr)
51
+
52
+ return if array_ptr.null?
53
+
54
+ array_ptr.read_array_of_pointer(count_ptr.read(:size_t)).each do |group_ptr|
55
+ @groups << build_group(group_ptr)
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ # @param group_ptr [FFI::Pointer] pointer to the `rd_kafka_ConsumerGroupDescription_t`
62
+ # @return [Hash]
63
+ def build_group(group_ptr)
64
+ state = Bindings.rd_kafka_ConsumerGroupDescription_state(group_ptr)
65
+ type = Bindings.rd_kafka_ConsumerGroupDescription_type(group_ptr)
66
+
67
+ {
68
+ group_id: read_string(Bindings.rd_kafka_ConsumerGroupDescription_group_id(group_ptr)),
69
+ error: build_error(Bindings.rd_kafka_ConsumerGroupDescription_error(group_ptr)),
70
+ is_simple_consumer_group:
71
+ Bindings.rd_kafka_ConsumerGroupDescription_is_simple_consumer_group(group_ptr) != 0,
72
+ state: state,
73
+ state_name: read_string(Bindings.rd_kafka_consumer_group_state_name(state)),
74
+ type: type,
75
+ type_name: read_string(Bindings.rd_kafka_consumer_group_type_name(type)),
76
+ partition_assignor:
77
+ read_string(Bindings.rd_kafka_ConsumerGroupDescription_partition_assignor(group_ptr)),
78
+ coordinator: extract_node(Bindings.rd_kafka_ConsumerGroupDescription_coordinator(group_ptr)),
79
+ authorized_operations: build_authorized_operations(group_ptr),
80
+ members: Array.new(Bindings.rd_kafka_ConsumerGroupDescription_member_count(group_ptr)) do |index|
81
+ build_member(Bindings.rd_kafka_ConsumerGroupDescription_member(group_ptr, index))
82
+ end
83
+ }
84
+ end
85
+
86
+ # @param member_ptr [FFI::Pointer] pointer to the `rd_kafka_MemberDescription_t`
87
+ # @return [Hash]
88
+ def build_member(member_ptr)
89
+ {
90
+ member_id: read_string(Bindings.rd_kafka_MemberDescription_consumer_id(member_ptr)),
91
+ client_id: read_string(Bindings.rd_kafka_MemberDescription_client_id(member_ptr)),
92
+ group_instance_id: read_string(Bindings.rd_kafka_MemberDescription_group_instance_id(member_ptr)),
93
+ host: read_string(Bindings.rd_kafka_MemberDescription_host(member_ptr)),
94
+ assignment: build_assignment(Bindings.rd_kafka_MemberDescription_assignment(member_ptr)),
95
+ target_assignment: build_target_assignment(member_ptr)
96
+ }
97
+ end
98
+
99
+ # @param assignment_ptr [FFI::Pointer] pointer to the `rd_kafka_MemberAssignment_t`
100
+ # @return [Hash{String => Array<Integer>}]
101
+ def build_assignment(assignment_ptr)
102
+ return {} if assignment_ptr.null?
103
+
104
+ tpl_ptr = Bindings.rd_kafka_MemberAssignment_partitions(assignment_ptr)
105
+
106
+ return {} if tpl_ptr.null?
107
+
108
+ Consumer::TopicPartitionList
109
+ .from_native_tpl(tpl_ptr)
110
+ .to_h
111
+ .transform_values { |partitions| (partitions || []).map(&:partition) }
112
+ end
113
+
114
+ # @param member_ptr [FFI::Pointer] pointer to the `rd_kafka_MemberDescription_t`
115
+ # @return [Hash{String => Array<Integer>}, nil]
116
+ def build_target_assignment(member_ptr)
117
+ assignment_ptr = Bindings.rd_kafka_MemberDescription_target_assignment(member_ptr)
118
+
119
+ assignment_ptr.null? ? nil : build_assignment(assignment_ptr)
120
+ end
121
+
122
+ # @param group_ptr [FFI::Pointer] pointer to the `rd_kafka_ConsumerGroupDescription_t`
123
+ # @return [Array<Integer>, nil]
124
+ def build_authorized_operations(group_ptr)
125
+ count_ptr = FFI::MemoryPointer.new(:size_t)
126
+
127
+ extract_authorized_operations(
128
+ Bindings.rd_kafka_ConsumerGroupDescription_authorized_operations(group_ptr, count_ptr),
129
+ count_ptr
130
+ )
131
+ end
132
+
133
+ # @param error_ptr [FFI::Pointer] pointer to the `rd_kafka_error_t`
134
+ # @return [RdkafkaError, nil]
135
+ def build_error(error_ptr)
136
+ return nil if error_ptr.null?
137
+
138
+ RdkafkaError.new(
139
+ Bindings.rd_kafka_error_code(error_ptr),
140
+ broker_message: read_string(Bindings.rd_kafka_error_string(error_ptr))
141
+ )
142
+ end
143
+
144
+ # @param ptr [FFI::Pointer] pointer to a C string
145
+ # @return [String, nil]
146
+ def read_string(ptr)
147
+ ptr.null? ? nil : ptr.read_string
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rdkafka
4
+ class Admin
5
+ # Helpers shared by the describe-topics and describe-cluster reports to copy nodes and
6
+ # authorized operations out of event-owned memory.
7
+ # @private
8
+ module DescribeResultParsing
9
+ private
10
+
11
+ # @param node_ptr [FFI::Pointer] pointer to a `rd_kafka_Node_t`
12
+ # @return [Hash, nil] node with `:id`, `:host`, `:port` and `:rack` or nil when NULL
13
+ def extract_node(node_ptr)
14
+ return nil if node_ptr.null?
15
+
16
+ host_ptr = Bindings.rd_kafka_Node_host(node_ptr)
17
+ rack_ptr = Bindings.rd_kafka_Node_rack(node_ptr)
18
+
19
+ {
20
+ id: Bindings.rd_kafka_Node_id(node_ptr),
21
+ host: host_ptr.null? ? nil : host_ptr.read_string,
22
+ port: Bindings.rd_kafka_Node_port(node_ptr),
23
+ rack: rack_ptr.null? ? nil : rack_ptr.read_string
24
+ }
25
+ end
26
+
27
+ # @param array_ptr [FFI::Pointer] pointer to a `rd_kafka_Node_t*` array
28
+ # @param count_ptr [FFI::MemoryPointer] size_t holding the array length
29
+ # @return [Array<Hash>] nodes as returned by {#extract_node}
30
+ def extract_nodes(array_ptr, count_ptr)
31
+ return [] if array_ptr.null?
32
+
33
+ array_ptr.read_array_of_pointer(count_ptr.read(:size_t)).map { |node_ptr| extract_node(node_ptr) }
34
+ end
35
+
36
+ # @param array_ptr [FFI::Pointer] pointer to a `rd_kafka_AclOperation_t` array
37
+ # @param count_ptr [FFI::MemoryPointer] size_t holding the array length
38
+ # @return [Array<Integer>, nil] `Bindings::RD_KAFKA_ACL_OPERATION_*` codes or nil when the
39
+ # authorized operations were not requested
40
+ def extract_authorized_operations(array_ptr, count_ptr)
41
+ return nil if array_ptr.null?
42
+
43
+ array_ptr.read_array_of_int(count_ptr.read(:size_t))
44
+ end
45
+ end
46
+ end
47
+ end
@@ -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
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rdkafka
4
+ class Admin
5
+ # Handle for list consumer group offsets operation
6
+ class ListConsumerGroupOffsetsHandle < AbstractHandle
7
+ layout :pending, :bool,
8
+ :response, :int
9
+
10
+ # @return [String] the name of the operation
11
+ def operation_name
12
+ "list consumer group offsets"
13
+ end
14
+
15
+ # Creates the result report
16
+ # @return [ListConsumerGroupOffsetsReport]
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,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rdkafka
4
+ class Admin
5
+ # Report for list consumer group offsets operation result
6
+ class ListConsumerGroupOffsetsReport
7
+ # @return [String, nil] the group the operation ran against
8
+ attr_reader :group_name
9
+
10
+ # @return [String, nil] group level error message, if any
11
+ attr_reader :error_string
12
+
13
+ # Committed offsets per partition, each with `:topic`, `:partition`, `:offset` (nil when
14
+ # the group has no committed offset for it or the partition has an `:error`), `:metadata`
15
+ # (nil when none was committed) and `:error` (an `RdkafkaError` or nil).
16
+ # @return [Array<Hash>]
17
+ attr_reader :partitions
18
+
19
+ # @param error_string [FFI::Pointer] pointer to the group level error string
20
+ # @param result_name [FFI::Pointer] pointer to the group name
21
+ # @param partitions_ptr [FFI::Pointer] pointer to the per partition result list
22
+ def initialize(error_string, result_name, partitions_ptr = FFI::Pointer::NULL)
23
+ @error_string = error_string.null? ? nil : error_string.read_string
24
+ @group_name = result_name.null? ? nil : result_name.read_string
25
+ @partitions = extract_partitions(partitions_ptr)
26
+ end
27
+
28
+ private
29
+
30
+ # @param partitions_ptr [FFI::Pointer] pointer to a native topic partition list
31
+ # @return [Array<Hash>]
32
+ def extract_partitions(partitions_ptr)
33
+ return [] if partitions_ptr.nil? || partitions_ptr.null?
34
+
35
+ native = Bindings::TopicPartitionList.new(partitions_ptr)
36
+
37
+ Array.new(native[:cnt]) do |i|
38
+ tp = Bindings::TopicPartition.new(native[:elems] + (i * Bindings::TopicPartition.size))
39
+ offset = tp[:offset]
40
+
41
+ {
42
+ topic: tp[:topic],
43
+ partition: tp[:partition],
44
+ offset: (offset == Bindings::RD_KAFKA_OFFSET_INVALID) ? nil : offset,
45
+ metadata: tp[:metadata].null? ? nil : tp[:metadata].read_string(tp[:metadata_size]),
46
+ error: tp[:err].zero? ? nil : RdkafkaError.new(tp[:err])
47
+ }
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end