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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +100 -36
- data/README.md +3 -1
- data/docker-compose-ssl.yml +1 -0
- data/docker-compose.yml +1 -0
- data/ext/librdkafka.so +0 -0
- data/karafka-rdkafka.gemspec +5 -4
- data/lib/rdkafka/admin/acl_binding_result.rb +4 -16
- data/lib/rdkafka/admin/alter_consumer_group_offsets_report.rb +2 -2
- data/lib/rdkafka/admin/create_acl_report.rb +1 -3
- data/lib/rdkafka/admin/create_topic_report.rb +2 -6
- data/lib/rdkafka/admin/delete_consumer_group_offsets_report.rb +2 -2
- data/lib/rdkafka/admin/delete_groups_report.rb +2 -6
- data/lib/rdkafka/admin/delete_topic_report.rb +2 -6
- data/lib/rdkafka/admin/describe_cluster_handle.rb +31 -0
- data/lib/rdkafka/admin/describe_cluster_report.rb +51 -0
- data/lib/rdkafka/admin/describe_consumer_groups_handle.rb +31 -0
- data/lib/rdkafka/admin/describe_consumer_groups_report.rb +151 -0
- data/lib/rdkafka/admin/describe_result_parsing.rb +47 -0
- data/lib/rdkafka/admin/describe_topics_handle.rb +31 -0
- data/lib/rdkafka/admin/describe_topics_report.rb +118 -0
- data/lib/rdkafka/admin/list_consumer_group_offsets_handle.rb +31 -0
- data/lib/rdkafka/admin/list_consumer_group_offsets_report.rb +52 -0
- data/lib/rdkafka/admin.rb +424 -9
- data/lib/rdkafka/bindings/admin/acls.rb +85 -0
- data/lib/rdkafka/bindings/admin/configs.rb +37 -0
- data/lib/rdkafka/bindings/admin/list_offsets.rb +26 -0
- data/lib/rdkafka/bindings/admin.rb +211 -0
- data/lib/rdkafka/bindings/callbacks.rb +155 -0
- data/lib/rdkafka/bindings/configuration.rb +50 -0
- data/lib/rdkafka/bindings/consumer.rb +125 -0
- data/lib/rdkafka/bindings/errors.rb +17 -0
- data/lib/rdkafka/bindings/handle.rb +16 -0
- data/lib/rdkafka/bindings/messages.rb +28 -0
- data/lib/rdkafka/bindings/metadata.rb +17 -0
- data/lib/rdkafka/bindings/polling.rb +17 -0
- data/lib/rdkafka/bindings/producer.rb +82 -0
- data/lib/rdkafka/bindings/share_consumer.rb +67 -0
- data/lib/rdkafka/bindings/structs.rb +62 -0
- data/lib/rdkafka/bindings.rb +16 -775
- data/lib/rdkafka/callbacks/describe_cluster_handler.rb +36 -0
- data/lib/rdkafka/callbacks/describe_consumer_groups_handler.rb +36 -0
- data/lib/rdkafka/callbacks/describe_topics_handler.rb +36 -0
- data/lib/rdkafka/callbacks/list_consumer_group_offsets_handler.rb +62 -0
- data/lib/rdkafka/callbacks.rb +15 -7
- data/lib/rdkafka/clients.rb +13 -46
- data/lib/rdkafka/config.rb +70 -0
- data/lib/rdkafka/consumer/allocator.rb +0 -2
- data/lib/rdkafka/consumer/message.rb +6 -9
- data/lib/rdkafka/consumer/topic_partition_list.rb +7 -7
- data/lib/rdkafka/consumer.rb +76 -36
- data/lib/rdkafka/defaults.rb +6 -4
- data/lib/rdkafka/native_kafka.rb +45 -31
- data/lib/rdkafka/producer/delivery_handle.rb +18 -16
- data/lib/rdkafka/producer/delivery_report.rb +51 -1
- data/lib/rdkafka/producer/partitions_count_cache.rb +109 -26
- data/lib/rdkafka/producer.rb +141 -71
- data/lib/rdkafka/share_consumer.rb +70 -34
- data/lib/rdkafka/version.rb +3 -3
- data/lib/rdkafka.rb +14 -1
- data/package-lock.json +9 -9
- data/renovate.json +7 -0
- 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
|