rdkafka 0.29.2 → 0.31.0
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 +42 -15
- data/README.md +2 -0
- data/dist/librdkafka-2.15.1.tar.gz +0 -0
- data/docker-compose-ssl.yml +1 -0
- data/docker-compose.yml +1 -0
- data/lib/rdkafka/admin/alter_consumer_group_offsets_handle.rb +31 -0
- data/lib/rdkafka/admin/alter_consumer_group_offsets_report.rb +50 -0
- data/lib/rdkafka/admin/delete_consumer_group_offsets_handle.rb +31 -0
- data/lib/rdkafka/admin/delete_consumer_group_offsets_report.rb +50 -0
- data/lib/rdkafka/admin/describe_cluster_handle.rb +31 -0
- data/lib/rdkafka/admin/describe_cluster_report.rb +48 -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.rb +365 -0
- 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 +197 -0
- data/lib/rdkafka/bindings/callbacks.rb +100 -0
- data/lib/rdkafka/bindings/configuration.rb +50 -0
- data/lib/rdkafka/bindings/consumer.rb +83 -0
- data/lib/rdkafka/bindings/errors.rb +15 -0
- data/lib/rdkafka/bindings/handle.rb +16 -0
- data/lib/rdkafka/bindings/messages.rb +28 -0
- data/lib/rdkafka/bindings/metadata.rb +14 -0
- data/lib/rdkafka/bindings/polling.rb +17 -0
- data/lib/rdkafka/bindings/producer.rb +77 -0
- data/lib/rdkafka/bindings/structs.rb +62 -0
- data/lib/rdkafka/bindings.rb +12 -612
- data/lib/rdkafka/callbacks/alter_consumer_group_offsets_handler.rb +65 -0
- data/lib/rdkafka/callbacks/delete_consumer_group_offsets_handler.rb +65 -0
- 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.rb +24 -17
- data/lib/rdkafka/clients.rb +13 -46
- data/lib/rdkafka/config.rb +34 -0
- data/lib/rdkafka/consumer/message.rb +6 -9
- data/lib/rdkafka/producer/delivery_handle.rb +8 -2
- data/lib/rdkafka/producer/delivery_report.rb +51 -1
- data/lib/rdkafka/version.rb +3 -3
- data/lib/rdkafka.rb +16 -0
- data/package-lock.json +9 -9
- data/rdkafka.gemspec +1 -1
- data/renovate.json +3 -0
- metadata +34 -4
- data/dist/librdkafka-2.14.2.tar.gz +0 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
module Callbacks
|
|
5
|
+
# Handles `RD_KAFKA_EVENT_ALTERCONSUMERGROUPOFFSETS_RESULT` events
|
|
6
|
+
# @private
|
|
7
|
+
class AlterConsumerGroupOffsetsHandler < BaseHandler
|
|
8
|
+
class << self
|
|
9
|
+
# Resolves the alter consumer group offsets handle from its result event
|
|
10
|
+
# @param event_ptr [FFI::Pointer] pointer to the event
|
|
11
|
+
# @return [void]
|
|
12
|
+
def call(event_ptr)
|
|
13
|
+
result = Rdkafka::Bindings.rd_kafka_event_AlterConsumerGroupOffsets_result(event_ptr)
|
|
14
|
+
|
|
15
|
+
pointer_to_size_t = FFI::MemoryPointer.new(:size_t)
|
|
16
|
+
group_result_array = Rdkafka::Bindings.rd_kafka_AlterConsumerGroupOffsets_result_groups(result, pointer_to_size_t)
|
|
17
|
+
group_results = GroupResult.create_group_results_from_array(pointer_to_size_t.read_int, group_result_array)
|
|
18
|
+
handle_ptr = Rdkafka::Bindings.rd_kafka_event_opaque(event_ptr)
|
|
19
|
+
|
|
20
|
+
if (handle = Rdkafka::Admin::AlterConsumerGroupOffsetsHandle.remove(handle_ptr.address))
|
|
21
|
+
unless resolve_operation_error(event_ptr, handle)
|
|
22
|
+
group_result = group_results[0]
|
|
23
|
+
|
|
24
|
+
# librdkafka only supports one group per invocation, so a missing group result
|
|
25
|
+
# means the broker returned nothing to act on rather than a partial success.
|
|
26
|
+
if group_result.nil?
|
|
27
|
+
handle[:response] = Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
28
|
+
handle.result = Rdkafka::Admin::AlterConsumerGroupOffsetsReport.new(
|
|
29
|
+
FFI::Pointer::NULL,
|
|
30
|
+
FFI::Pointer::NULL
|
|
31
|
+
)
|
|
32
|
+
else
|
|
33
|
+
report = Rdkafka::Admin::AlterConsumerGroupOffsetsReport.new(
|
|
34
|
+
group_result.error_string,
|
|
35
|
+
group_result.result_name,
|
|
36
|
+
Rdkafka::Bindings.rd_kafka_group_result_partitions(group_result_array.read_pointer)
|
|
37
|
+
)
|
|
38
|
+
handle.result = report
|
|
39
|
+
|
|
40
|
+
# Kafka reports a rejected offset change per partition, leaving the group level
|
|
41
|
+
# error unset - a group with live members comes back with the group error NULL
|
|
42
|
+
# and `unknown_member_id` on each partition. Resolving on the group error alone
|
|
43
|
+
# would hand back a report that looks like success, so any failed partition has
|
|
44
|
+
# to fail the handle too.
|
|
45
|
+
failed = report.partitions.find { |partition| partition[:error] }
|
|
46
|
+
|
|
47
|
+
if !group_result.result_error.zero?
|
|
48
|
+
handle[:response] = group_result.result_error
|
|
49
|
+
handle.broker_message = report.error_string
|
|
50
|
+
elsif failed
|
|
51
|
+
handle[:response] = failed[:error].rdkafka_response
|
|
52
|
+
handle.broker_message = "Failed for partition #{failed[:partition]} of '#{failed[:topic]}'"
|
|
53
|
+
else
|
|
54
|
+
handle[:response] = Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
handle.unlock
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
module Callbacks
|
|
5
|
+
# Handles `RD_KAFKA_EVENT_DELETECONSUMERGROUPOFFSETS_RESULT` events
|
|
6
|
+
# @private
|
|
7
|
+
class DeleteConsumerGroupOffsetsHandler < BaseHandler
|
|
8
|
+
class << self
|
|
9
|
+
# Resolves the delete consumer group offsets handle from its result event
|
|
10
|
+
# @param event_ptr [FFI::Pointer] pointer to the event
|
|
11
|
+
# @return [void]
|
|
12
|
+
def call(event_ptr)
|
|
13
|
+
result = Rdkafka::Bindings.rd_kafka_event_DeleteConsumerGroupOffsets_result(event_ptr)
|
|
14
|
+
|
|
15
|
+
pointer_to_size_t = FFI::MemoryPointer.new(:size_t)
|
|
16
|
+
group_result_array = Rdkafka::Bindings.rd_kafka_DeleteConsumerGroupOffsets_result_groups(result, pointer_to_size_t)
|
|
17
|
+
group_results = GroupResult.create_group_results_from_array(pointer_to_size_t.read_int, group_result_array)
|
|
18
|
+
handle_ptr = Rdkafka::Bindings.rd_kafka_event_opaque(event_ptr)
|
|
19
|
+
|
|
20
|
+
if (handle = Rdkafka::Admin::DeleteConsumerGroupOffsetsHandle.remove(handle_ptr.address))
|
|
21
|
+
unless resolve_operation_error(event_ptr, handle)
|
|
22
|
+
group_result = group_results[0]
|
|
23
|
+
|
|
24
|
+
# librdkafka only supports one group per invocation, so a missing group result
|
|
25
|
+
# means the broker returned nothing to act on rather than a partial success.
|
|
26
|
+
if group_result.nil?
|
|
27
|
+
handle[:response] = Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
28
|
+
handle.result = Rdkafka::Admin::DeleteConsumerGroupOffsetsReport.new(
|
|
29
|
+
FFI::Pointer::NULL,
|
|
30
|
+
FFI::Pointer::NULL
|
|
31
|
+
)
|
|
32
|
+
else
|
|
33
|
+
report = Rdkafka::Admin::DeleteConsumerGroupOffsetsReport.new(
|
|
34
|
+
group_result.error_string,
|
|
35
|
+
group_result.result_name,
|
|
36
|
+
Rdkafka::Bindings.rd_kafka_group_result_partitions(group_result_array.read_pointer)
|
|
37
|
+
)
|
|
38
|
+
handle.result = report
|
|
39
|
+
|
|
40
|
+
# Kafka reports a rejected offset change per partition, leaving the group level
|
|
41
|
+
# error unset - a group with live members comes back with the group error NULL
|
|
42
|
+
# and `unknown_member_id` on each partition. Resolving on the group error alone
|
|
43
|
+
# would hand back a report that looks like success, so any failed partition has
|
|
44
|
+
# to fail the handle too.
|
|
45
|
+
failed = report.partitions.find { |partition| partition[:error] }
|
|
46
|
+
|
|
47
|
+
if !group_result.result_error.zero?
|
|
48
|
+
handle[:response] = group_result.result_error
|
|
49
|
+
handle.broker_message = report.error_string
|
|
50
|
+
elsif failed
|
|
51
|
+
handle[:response] = failed[:error].rdkafka_response
|
|
52
|
+
handle.broker_message = "Failed for partition #{failed[:partition]} of '#{failed[:topic]}'"
|
|
53
|
+
else
|
|
54
|
+
handle[:response] = Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
handle.unlock
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
module Callbacks
|
|
5
|
+
# Handles `RD_KAFKA_EVENT_DESCRIBECLUSTER_RESULT` events
|
|
6
|
+
# @private
|
|
7
|
+
class DescribeClusterHandler < BaseHandler
|
|
8
|
+
class << self
|
|
9
|
+
# Resolves the describe cluster handle from its result event
|
|
10
|
+
# @param event_ptr [FFI::Pointer] pointer to the event
|
|
11
|
+
# @return [void]
|
|
12
|
+
def call(event_ptr)
|
|
13
|
+
result_ptr = Rdkafka::Bindings.rd_kafka_event_DescribeCluster_result(event_ptr)
|
|
14
|
+
handle_ptr = Rdkafka::Bindings.rd_kafka_event_opaque(event_ptr)
|
|
15
|
+
|
|
16
|
+
return unless (handle = Rdkafka::Admin::DescribeClusterHandle.remove(handle_ptr.address))
|
|
17
|
+
|
|
18
|
+
return if resolve_operation_error(event_ptr, handle)
|
|
19
|
+
|
|
20
|
+
handle[:response] = Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
21
|
+
|
|
22
|
+
# Parsing must copy everything out of event-owned memory before the event is destroyed.
|
|
23
|
+
# An exception here is captured and re-raised on the waiting thread, since it cannot
|
|
24
|
+
# unwind through librdkafka native frames.
|
|
25
|
+
handle.result = begin
|
|
26
|
+
Rdkafka::Admin::DescribeClusterReport.new(result_ptr)
|
|
27
|
+
rescue => e
|
|
28
|
+
e
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
handle.unlock
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
module Callbacks
|
|
5
|
+
# Handles `RD_KAFKA_EVENT_DESCRIBECONSUMERGROUPS_RESULT` events
|
|
6
|
+
# @private
|
|
7
|
+
class DescribeConsumerGroupsHandler < BaseHandler
|
|
8
|
+
class << self
|
|
9
|
+
# Resolves the describe-consumer-groups handle from its result event
|
|
10
|
+
# @param event_ptr [FFI::Pointer] pointer to the event
|
|
11
|
+
# @return [void]
|
|
12
|
+
def call(event_ptr)
|
|
13
|
+
result_ptr = Rdkafka::Bindings.rd_kafka_event_DescribeConsumerGroups_result(event_ptr)
|
|
14
|
+
handle_ptr = Rdkafka::Bindings.rd_kafka_event_opaque(event_ptr)
|
|
15
|
+
|
|
16
|
+
return unless (handle = Rdkafka::Admin::DescribeConsumerGroupsHandle.remove(handle_ptr.address))
|
|
17
|
+
|
|
18
|
+
return if resolve_operation_error(event_ptr, handle)
|
|
19
|
+
|
|
20
|
+
handle[:response] = Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
21
|
+
|
|
22
|
+
# Parsing must copy everything out of event-owned memory before the event is destroyed.
|
|
23
|
+
# An exception here is captured and re-raised on the waiting thread, since it cannot
|
|
24
|
+
# unwind through librdkafka native frames.
|
|
25
|
+
handle.result = begin
|
|
26
|
+
Rdkafka::Admin::DescribeConsumerGroupsReport.new(result_ptr)
|
|
27
|
+
rescue => e
|
|
28
|
+
e
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
handle.unlock
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
module Callbacks
|
|
5
|
+
# Handles `RD_KAFKA_EVENT_DESCRIBETOPICS_RESULT` events
|
|
6
|
+
# @private
|
|
7
|
+
class DescribeTopicsHandler < BaseHandler
|
|
8
|
+
class << self
|
|
9
|
+
# Resolves the describe topics handle from its result event
|
|
10
|
+
# @param event_ptr [FFI::Pointer] pointer to the event
|
|
11
|
+
# @return [void]
|
|
12
|
+
def call(event_ptr)
|
|
13
|
+
result_ptr = Rdkafka::Bindings.rd_kafka_event_DescribeTopics_result(event_ptr)
|
|
14
|
+
handle_ptr = Rdkafka::Bindings.rd_kafka_event_opaque(event_ptr)
|
|
15
|
+
|
|
16
|
+
return unless (handle = Rdkafka::Admin::DescribeTopicsHandle.remove(handle_ptr.address))
|
|
17
|
+
|
|
18
|
+
return if resolve_operation_error(event_ptr, handle)
|
|
19
|
+
|
|
20
|
+
handle[:response] = Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
21
|
+
|
|
22
|
+
# Parsing must copy everything out of event-owned memory before the event is destroyed.
|
|
23
|
+
# An exception here is captured and re-raised on the waiting thread, since it cannot
|
|
24
|
+
# unwind through librdkafka native frames.
|
|
25
|
+
handle.result = begin
|
|
26
|
+
Rdkafka::Admin::DescribeTopicsReport.new(result_ptr)
|
|
27
|
+
rescue => e
|
|
28
|
+
e
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
handle.unlock
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/rdkafka/callbacks.rb
CHANGED
|
@@ -21,10 +21,9 @@ module Rdkafka
|
|
|
21
21
|
# @param array_pointer [FFI::Pointer] pointer to the results array
|
|
22
22
|
# @return [Array<TopicResult>] array of topic results
|
|
23
23
|
def self.create_topic_results_from_array(count, array_pointer)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
end
|
|
24
|
+
return [] if count.zero?
|
|
25
|
+
|
|
26
|
+
array_pointer.read_array_of_pointer(count).map { |result_pointer| new(result_pointer) }
|
|
28
27
|
end
|
|
29
28
|
end
|
|
30
29
|
|
|
@@ -53,10 +52,9 @@ module Rdkafka
|
|
|
53
52
|
# @param array_pointer [FFI::Pointer] pointer to the results array
|
|
54
53
|
# @return [Array<GroupResult>] array of group results
|
|
55
54
|
def self.create_group_results_from_array(count, array_pointer)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
end
|
|
55
|
+
return [] if count.zero?
|
|
56
|
+
|
|
57
|
+
array_pointer.read_array_of_pointer(count).map { |result_pointer| new(result_pointer) }
|
|
60
58
|
end
|
|
61
59
|
end
|
|
62
60
|
|
|
@@ -77,10 +75,9 @@ module Rdkafka
|
|
|
77
75
|
# @param array_pointer [FFI::Pointer] pointer to the results array
|
|
78
76
|
# @return [Array<CreateAclResult>] array of ACL results
|
|
79
77
|
def self.create_acl_results_from_array(count, array_pointer)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
end
|
|
78
|
+
return [] if count.zero?
|
|
79
|
+
|
|
80
|
+
array_pointer.read_array_of_pointer(count).map { |result_pointer| new(result_pointer) }
|
|
84
81
|
end
|
|
85
82
|
end
|
|
86
83
|
|
|
@@ -108,10 +105,9 @@ module Rdkafka
|
|
|
108
105
|
# @param array_pointer [FFI::Pointer] pointer to the results array
|
|
109
106
|
# @return [Array<DeleteAclResult>] array of delete ACL results
|
|
110
107
|
def self.delete_acl_results_from_array(count, array_pointer)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
end
|
|
108
|
+
return [] if count.zero?
|
|
109
|
+
|
|
110
|
+
array_pointer.read_array_of_pointer(count).map { |result_pointer| new(result_pointer) }
|
|
115
111
|
end
|
|
116
112
|
end
|
|
117
113
|
|
|
@@ -213,6 +209,8 @@ module Rdkafka
|
|
|
213
209
|
when Rdkafka::Bindings::RD_KAFKA_EVENT_DELETETOPICS_RESULT then DeleteTopicHandler
|
|
214
210
|
when Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_CREATEPARTITIONS_RESULT then CreatePartitionsHandler
|
|
215
211
|
when Rdkafka::Bindings::RD_KAFKA_EVENT_DELETEGROUPS_RESULT then DeleteGroupsHandler
|
|
212
|
+
when Rdkafka::Bindings::RD_KAFKA_EVENT_ALTERCONSUMERGROUPOFFSETS_RESULT then AlterConsumerGroupOffsetsHandler
|
|
213
|
+
when Rdkafka::Bindings::RD_KAFKA_EVENT_DELETECONSUMERGROUPOFFSETS_RESULT then DeleteConsumerGroupOffsetsHandler
|
|
216
214
|
when Rdkafka::Bindings::RD_KAFKA_EVENT_DELETERECORDS_RESULT then DeleteRecordsHandler
|
|
217
215
|
when Rdkafka::Bindings::RD_KAFKA_EVENT_CREATEACLS_RESULT then CreateAclHandler
|
|
218
216
|
when Rdkafka::Bindings::RD_KAFKA_EVENT_DELETEACLS_RESULT then DeleteAclHandler
|
|
@@ -221,6 +219,9 @@ module Rdkafka
|
|
|
221
219
|
when Rdkafka::Bindings::RD_KAFKA_EVENT_INCREMENTALALTERCONFIGS_RESULT then IncrementalAlterConfigsHandler
|
|
222
220
|
when Rdkafka::Bindings::RD_KAFKA_EVENT_LISTOFFSETS_RESULT then ListOffsetsHandler
|
|
223
221
|
when Rdkafka::Bindings::RD_KAFKA_EVENT_LISTCONSUMERGROUPS_RESULT then ListConsumerGroupsHandler
|
|
222
|
+
when Rdkafka::Bindings::RD_KAFKA_EVENT_DESCRIBECONSUMERGROUPS_RESULT then DescribeConsumerGroupsHandler
|
|
223
|
+
when Rdkafka::Bindings::RD_KAFKA_EVENT_DESCRIBECLUSTER_RESULT then DescribeClusterHandler
|
|
224
|
+
when Rdkafka::Bindings::RD_KAFKA_EVENT_DESCRIBETOPICS_RESULT then DescribeTopicsHandler
|
|
224
225
|
end
|
|
225
226
|
|
|
226
227
|
handler&.call(event_ptr)
|
|
@@ -249,6 +250,9 @@ module Rdkafka
|
|
|
249
250
|
delivery_handle[:response] = message[:err]
|
|
250
251
|
delivery_handle[:partition] = message[:partition]
|
|
251
252
|
delivery_handle[:offset] = message[:offset]
|
|
253
|
+
delivery_handle[:status] = Rdkafka::Bindings.rd_kafka_message_status(message_ptr)
|
|
254
|
+
delivery_handle[:latency] = Rdkafka::Bindings.rd_kafka_message_latency(message_ptr)
|
|
255
|
+
delivery_handle[:broker_id] = Rdkafka::Bindings.rd_kafka_message_broker_id(message_ptr)
|
|
252
256
|
# The topic is not stored on the handle here: it is already set as the handle's
|
|
253
257
|
# `topic` Ruby attribute during produce, which spares a native string copy that
|
|
254
258
|
# would otherwise be allocated and retained for every message.
|
|
@@ -262,7 +266,10 @@ module Rdkafka
|
|
|
262
266
|
message[:offset],
|
|
263
267
|
topic_name,
|
|
264
268
|
message[:err],
|
|
265
|
-
delivery_handle.label
|
|
269
|
+
delivery_handle.label,
|
|
270
|
+
status: delivery_handle[:status],
|
|
271
|
+
latency: delivery_handle[:latency],
|
|
272
|
+
broker_id: delivery_handle[:broker_id]
|
|
266
273
|
),
|
|
267
274
|
delivery_handle
|
|
268
275
|
)
|
data/lib/rdkafka/clients.rb
CHANGED
|
@@ -4,22 +4,10 @@ module Rdkafka
|
|
|
4
4
|
# Tracks live public clients so they can be closed before Ruby begins forced finalization.
|
|
5
5
|
# @private
|
|
6
6
|
module Clients
|
|
7
|
-
# Ruby rewrote `ObjectSpace::WeakMap` in 3.3. On 3.2 and earlier its `[]` can hand back a
|
|
8
|
-
# foreign or already-freed object for a key, which raises `NoMethodError` or segfaults the VM
|
|
9
|
-
# the moment the value is touched. Track weakly only where that is safe and fall back to a
|
|
10
|
-
# strong registry - pruned as clients close - on older Rubies. The strong registry holds an
|
|
11
|
-
# open, un-closed client until shutdown, which is exactly when this cleanup needs it anyway.
|
|
12
|
-
WEAK_TRACKING = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.3")
|
|
13
|
-
|
|
14
7
|
@pid = Process.pid
|
|
15
8
|
@mutex = Mutex.new
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@clients = ObjectSpace::WeakMap.new
|
|
19
|
-
@tokens = [].freeze
|
|
20
|
-
else
|
|
21
|
-
@clients = {}.freeze
|
|
22
|
-
end
|
|
9
|
+
@clients = ObjectSpace::WeakMap.new
|
|
10
|
+
@tokens = [].freeze
|
|
23
11
|
|
|
24
12
|
# Registers a successfully constructed client so it can be closed during process shutdown.
|
|
25
13
|
# @param client [Consumer, Producer, Admin] client to close during process shutdown
|
|
@@ -28,21 +16,14 @@ module Rdkafka
|
|
|
28
16
|
reset_after_fork
|
|
29
17
|
|
|
30
18
|
@mutex.synchronize do
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@tokens = (@tokens + [token]).freeze
|
|
40
|
-
else
|
|
41
|
-
# Strong references, so drop clients that have already been closed to bound growth.
|
|
42
|
-
live = @clients.reject { |_id, existing| existing.closed? }
|
|
43
|
-
live[client.object_id] = client
|
|
44
|
-
@clients = live.freeze
|
|
45
|
-
end
|
|
19
|
+
live_clients
|
|
20
|
+
|
|
21
|
+
# The strongly-held token is the key and the client is the weak value, so registering a
|
|
22
|
+
# client never keeps it alive. Enumerating weak keys can expose reclaimed slots, so we
|
|
23
|
+
# look the value up by token instead.
|
|
24
|
+
token = Object.new
|
|
25
|
+
@clients[token] = client
|
|
26
|
+
@tokens = (@tokens + [token]).freeze
|
|
46
27
|
end
|
|
47
28
|
|
|
48
29
|
client
|
|
@@ -54,7 +35,7 @@ module Rdkafka
|
|
|
54
35
|
def self.close_all
|
|
55
36
|
return unless @pid == Process.pid
|
|
56
37
|
|
|
57
|
-
clients = @mutex.synchronize {
|
|
38
|
+
clients = @mutex.synchronize { live_clients }
|
|
58
39
|
first_error = nil
|
|
59
40
|
|
|
60
41
|
clients.each do |client|
|
|
@@ -66,16 +47,7 @@ module Rdkafka
|
|
|
66
47
|
raise first_error if first_error
|
|
67
48
|
end
|
|
68
49
|
|
|
69
|
-
# Returns the currently registered live clients.
|
|
70
|
-
# @return [Array<Consumer, Producer, Admin>] live clients
|
|
71
|
-
# @private
|
|
72
|
-
def self.current_clients
|
|
73
|
-
WEAK_TRACKING ? live_clients : @clients.values
|
|
74
|
-
end
|
|
75
|
-
private_class_method :current_clients
|
|
76
|
-
|
|
77
50
|
# Returns the live clients and releases tokens whose clients have been collected.
|
|
78
|
-
# Weak-tracking Rubies only.
|
|
79
51
|
# @return [Array<Consumer, Producer, Admin>] live clients
|
|
80
52
|
# @private
|
|
81
53
|
def self.live_clients
|
|
@@ -99,13 +71,8 @@ module Rdkafka
|
|
|
99
71
|
def self.reset_after_fork
|
|
100
72
|
return if @pid == Process.pid
|
|
101
73
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
@tokens = [].freeze
|
|
105
|
-
else
|
|
106
|
-
@clients = {}.freeze
|
|
107
|
-
end
|
|
108
|
-
|
|
74
|
+
@clients = ObjectSpace::WeakMap.new
|
|
75
|
+
@tokens = [].freeze
|
|
109
76
|
@pid = Process.pid
|
|
110
77
|
@mutex = Mutex.new
|
|
111
78
|
end
|
data/lib/rdkafka/config.rb
CHANGED
|
@@ -22,6 +22,12 @@ module Rdkafka
|
|
|
22
22
|
@@log_mutex = Mutex.new
|
|
23
23
|
# @!visibility private
|
|
24
24
|
@@oauthbearer_token_refresh_callback = nil
|
|
25
|
+
# @!visibility private
|
|
26
|
+
# Whether the partition key length passed to the partitioner is the byte length (`bytesize`)
|
|
27
|
+
# rather than the character length (`size`). `false` (character length) is the legacy default
|
|
28
|
+
# to avoid changing partition assignment for existing multibyte/binary keys; set to `true` to
|
|
29
|
+
# opt into the correct, other-client-consistent byte-length hashing.
|
|
30
|
+
@@partitioner_key_uses_bytesize = false
|
|
25
31
|
|
|
26
32
|
# Returns the current logger, by default this is a logger to stdout.
|
|
27
33
|
#
|
|
@@ -122,6 +128,34 @@ module Rdkafka
|
|
|
122
128
|
@@oauthbearer_token_refresh_callback
|
|
123
129
|
end
|
|
124
130
|
|
|
131
|
+
# Whether the partitioner hashes the partition key by its byte length instead of its character
|
|
132
|
+
# length. Defaults to `false` (character length) to preserve existing partition assignment for
|
|
133
|
+
# multibyte/binary keys. Passing the byte length is the correct, other-client-consistent
|
|
134
|
+
# behavior; enable it (and be aware it can move such keys to a different partition) with
|
|
135
|
+
# `Rdkafka::Config.partitioner_key_uses_bytesize = true`. This default is expected to change in
|
|
136
|
+
# a future major version.
|
|
137
|
+
#
|
|
138
|
+
# @return [Boolean]
|
|
139
|
+
def self.partitioner_key_uses_bytesize
|
|
140
|
+
@@partitioner_key_uses_bytesize
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Sets whether the partitioner hashes the partition key by its byte length rather than its
|
|
144
|
+
# character length. The key length method is resolved here once rather than on every produced
|
|
145
|
+
# message, and each call re-resolves it, so changing the setting at runtime takes effect
|
|
146
|
+
# immediately.
|
|
147
|
+
#
|
|
148
|
+
# @param value [Boolean] true to hash by byte length, false (default) for character length
|
|
149
|
+
# @return [void]
|
|
150
|
+
def self.partitioner_key_uses_bytesize=(value)
|
|
151
|
+
@@partitioner_key_uses_bytesize = value
|
|
152
|
+
|
|
153
|
+
Rdkafka::Bindings.singleton_class.alias_method(
|
|
154
|
+
:partition_key_length,
|
|
155
|
+
value ? :partition_key_bytesize : :partition_key_size
|
|
156
|
+
)
|
|
157
|
+
end
|
|
158
|
+
|
|
125
159
|
# @private
|
|
126
160
|
def self.opaques
|
|
127
161
|
@@opaques
|
|
@@ -35,19 +35,16 @@ module Rdkafka
|
|
|
35
35
|
# @param native_message [Rdkafka::Bindings::Message] native message struct from librdkafka
|
|
36
36
|
def initialize(native_message)
|
|
37
37
|
# Set topic
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
end
|
|
38
|
+
rkt = native_message[:rkt]
|
|
39
|
+
@topic = Rdkafka::Bindings.rd_kafka_topic_name(rkt) unless rkt.null?
|
|
41
40
|
# Set partition
|
|
42
41
|
@partition = native_message[:partition]
|
|
43
42
|
# Set payload
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
end
|
|
43
|
+
payload = native_message[:payload]
|
|
44
|
+
@payload = payload.read_string(native_message[:len]) unless payload.null?
|
|
47
45
|
# Set key
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
end
|
|
46
|
+
key = native_message[:key]
|
|
47
|
+
@key = key.read_string(native_message[:key_len]) unless key.null?
|
|
51
48
|
# Set offset
|
|
52
49
|
@offset = native_message[:offset]
|
|
53
50
|
# Set timestamp
|
|
@@ -7,7 +7,10 @@ module Rdkafka
|
|
|
7
7
|
layout :pending, :bool,
|
|
8
8
|
:response, :int,
|
|
9
9
|
:partition, :int,
|
|
10
|
-
:offset, :int64
|
|
10
|
+
:offset, :int64,
|
|
11
|
+
:status, :int,
|
|
12
|
+
:latency, :int64,
|
|
13
|
+
:broker_id, :int32
|
|
11
14
|
|
|
12
15
|
# @return [Object, nil] label set during message production or nil by default
|
|
13
16
|
attr_accessor :label
|
|
@@ -31,7 +34,10 @@ module Rdkafka
|
|
|
31
34
|
# we should not return it
|
|
32
35
|
topic,
|
|
33
36
|
(self[:response] != 0) ? RdkafkaError.new(self[:response]) : nil,
|
|
34
|
-
label
|
|
37
|
+
label,
|
|
38
|
+
status: self[:status],
|
|
39
|
+
latency: self[:latency],
|
|
40
|
+
broker_id: self[:broker_id]
|
|
35
41
|
)
|
|
36
42
|
end
|
|
37
43
|
end
|
|
@@ -25,6 +25,26 @@ module Rdkafka
|
|
|
25
25
|
# @return [Object, nil] label set during message production or nil by default
|
|
26
26
|
attr_reader :label
|
|
27
27
|
|
|
28
|
+
# Persistence status of the message, one of `Bindings::RD_KAFKA_MSG_STATUS_*`:
|
|
29
|
+
# - `RD_KAFKA_MSG_STATUS_NOT_PERSISTED` - the message was not written to the broker. A retry
|
|
30
|
+
# risks reordering, but not a duplicate.
|
|
31
|
+
# - `RD_KAFKA_MSG_STATUS_POSSIBLY_PERSISTED` - the message may have been written (e.g. the
|
|
32
|
+
# request timed out before an ack). A retry risks reordering and a duplicate.
|
|
33
|
+
# - `RD_KAFKA_MSG_STATUS_PERSISTED` - the broker acknowledged the message. Trust this only
|
|
34
|
+
# with `acks=all`.
|
|
35
|
+
# `nil` when the report was not built from a delivery callback.
|
|
36
|
+
# @return [Integer, nil]
|
|
37
|
+
attr_reader :status
|
|
38
|
+
|
|
39
|
+
# Time from the `produce` call until the delivery report, in microseconds, or `nil` when
|
|
40
|
+
# not available
|
|
41
|
+
# @return [Integer, nil]
|
|
42
|
+
attr_reader :latency
|
|
43
|
+
|
|
44
|
+
# Id of the broker the message was produced to, or `nil` when not known
|
|
45
|
+
# @return [Integer, nil]
|
|
46
|
+
attr_reader :broker_id
|
|
47
|
+
|
|
28
48
|
# We alias the `#topic_name` under `#topic` to make this consistent with `Consumer::Message`
|
|
29
49
|
# where the topic name is under `#topic` method. That way we have a consistent name that
|
|
30
50
|
# is present in both places
|
|
@@ -38,12 +58,42 @@ module Rdkafka
|
|
|
38
58
|
# @param topic_name [String, nil] topic name
|
|
39
59
|
# @param error [Integer, nil] error code if any
|
|
40
60
|
# @param label [Object, nil] user-defined label
|
|
41
|
-
|
|
61
|
+
# @param status [Integer, nil] `Bindings::RD_KAFKA_MSG_STATUS_*` persistence status
|
|
62
|
+
# @param latency [Integer, nil] produce latency in microseconds, negative when not available
|
|
63
|
+
# @param broker_id [Integer, nil] broker id, negative when not known
|
|
64
|
+
def initialize(
|
|
65
|
+
partition,
|
|
66
|
+
offset,
|
|
67
|
+
topic_name = nil,
|
|
68
|
+
error = nil,
|
|
69
|
+
label = nil,
|
|
70
|
+
status: nil,
|
|
71
|
+
latency: nil,
|
|
72
|
+
broker_id: nil
|
|
73
|
+
)
|
|
42
74
|
@partition = partition
|
|
43
75
|
@offset = offset
|
|
44
76
|
@topic_name = topic_name
|
|
45
77
|
@error = error
|
|
46
78
|
@label = label
|
|
79
|
+
@status = status
|
|
80
|
+
@latency = (latency.nil? || latency.negative?) ? nil : latency
|
|
81
|
+
@broker_id = (broker_id.nil? || broker_id.negative?) ? nil : broker_id
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# @return [Boolean] true when the broker acknowledged the message
|
|
85
|
+
def persisted?
|
|
86
|
+
status == Bindings::RD_KAFKA_MSG_STATUS_PERSISTED
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# @return [Boolean] true when the message may have been written, so a retry may duplicate it
|
|
90
|
+
def possibly_persisted?
|
|
91
|
+
status == Bindings::RD_KAFKA_MSG_STATUS_POSSIBLY_PERSISTED
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# @return [Boolean] true when the message was not written, so a retry does not duplicate it
|
|
95
|
+
def not_persisted?
|
|
96
|
+
status == Bindings::RD_KAFKA_MSG_STATUS_NOT_PERSISTED
|
|
47
97
|
end
|
|
48
98
|
end
|
|
49
99
|
end
|
data/lib/rdkafka/version.rb
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module Rdkafka
|
|
4
4
|
# Current rdkafka-ruby gem version
|
|
5
|
-
VERSION = "0.
|
|
5
|
+
VERSION = "0.31.0"
|
|
6
6
|
# Target librdkafka version to be used
|
|
7
|
-
LIBRDKAFKA_VERSION = "2.
|
|
7
|
+
LIBRDKAFKA_VERSION = "2.15.1"
|
|
8
8
|
# SHA256 hash of the librdkafka source tarball for verification
|
|
9
|
-
LIBRDKAFKA_SOURCE_SHA256 = "
|
|
9
|
+
LIBRDKAFKA_SOURCE_SHA256 = "23c8575c7d1ced07246cb9cf200c11325b72201fd4134a02414ca869fbdd8ed3"
|
|
10
10
|
end
|
data/lib/rdkafka.rb
CHANGED
|
@@ -13,6 +13,10 @@ require "rdkafka/helpers/list_offsets"
|
|
|
13
13
|
require "rdkafka/defaults"
|
|
14
14
|
require "rdkafka/abstract_handle"
|
|
15
15
|
require "rdkafka/admin"
|
|
16
|
+
require "rdkafka/admin/alter_consumer_group_offsets_handle"
|
|
17
|
+
require "rdkafka/admin/alter_consumer_group_offsets_report"
|
|
18
|
+
require "rdkafka/admin/delete_consumer_group_offsets_handle"
|
|
19
|
+
require "rdkafka/admin/delete_consumer_group_offsets_report"
|
|
16
20
|
require "rdkafka/admin/create_topic_handle"
|
|
17
21
|
require "rdkafka/admin/create_topic_report"
|
|
18
22
|
require "rdkafka/admin/delete_groups_handle"
|
|
@@ -37,6 +41,13 @@ require "rdkafka/admin/list_offsets_handle"
|
|
|
37
41
|
require "rdkafka/admin/list_offsets_report"
|
|
38
42
|
require "rdkafka/admin/list_consumer_groups_handle"
|
|
39
43
|
require "rdkafka/admin/list_consumer_groups_report"
|
|
44
|
+
require "rdkafka/admin/describe_result_parsing"
|
|
45
|
+
require "rdkafka/admin/describe_cluster_handle"
|
|
46
|
+
require "rdkafka/admin/describe_cluster_report"
|
|
47
|
+
require "rdkafka/admin/describe_topics_handle"
|
|
48
|
+
require "rdkafka/admin/describe_topics_report"
|
|
49
|
+
require "rdkafka/admin/describe_consumer_groups_handle"
|
|
50
|
+
require "rdkafka/admin/describe_consumer_groups_report"
|
|
40
51
|
require "rdkafka/admin/acl_binding_result"
|
|
41
52
|
require "rdkafka/admin/config_binding_result"
|
|
42
53
|
require "rdkafka/admin/config_resource_binding_result"
|
|
@@ -46,6 +57,8 @@ require "rdkafka/callbacks/base_handler"
|
|
|
46
57
|
require "rdkafka/callbacks/create_topic_handler"
|
|
47
58
|
require "rdkafka/callbacks/delete_topic_handler"
|
|
48
59
|
require "rdkafka/callbacks/create_partitions_handler"
|
|
60
|
+
require "rdkafka/callbacks/alter_consumer_group_offsets_handler"
|
|
61
|
+
require "rdkafka/callbacks/delete_consumer_group_offsets_handler"
|
|
49
62
|
require "rdkafka/callbacks/delete_groups_handler"
|
|
50
63
|
require "rdkafka/callbacks/delete_records_handler"
|
|
51
64
|
require "rdkafka/callbacks/create_acl_handler"
|
|
@@ -55,6 +68,9 @@ require "rdkafka/callbacks/describe_configs_handler"
|
|
|
55
68
|
require "rdkafka/callbacks/incremental_alter_configs_handler"
|
|
56
69
|
require "rdkafka/callbacks/list_offsets_handler"
|
|
57
70
|
require "rdkafka/callbacks/list_consumer_groups_handler"
|
|
71
|
+
require "rdkafka/callbacks/describe_cluster_handler"
|
|
72
|
+
require "rdkafka/callbacks/describe_topics_handler"
|
|
73
|
+
require "rdkafka/callbacks/describe_consumer_groups_handler"
|
|
58
74
|
require "rdkafka/config"
|
|
59
75
|
require "rdkafka/consumer"
|
|
60
76
|
require "rdkafka/consumer/headers"
|