karafka-rdkafka 0.28.0-aarch64-linux-gnu → 0.28.2-aarch64-linux-gnu
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -30
- data/README.md +3 -3
- data/ext/librdkafka.so +0 -0
- data/lib/rdkafka/admin/create_acl_report.rb +2 -1
- data/lib/rdkafka/admin/delete_records_handle.rb +31 -0
- data/lib/rdkafka/admin/delete_records_report.rb +25 -0
- data/lib/rdkafka/admin/describe_acl_report.rb +2 -1
- data/lib/rdkafka/admin/list_consumer_groups_handle.rb +31 -0
- data/lib/rdkafka/admin/list_consumer_groups_report.rb +83 -0
- data/lib/rdkafka/admin.rb +317 -90
- data/lib/rdkafka/bindings.rb +52 -13
- data/lib/rdkafka/callbacks/delete_records_handler.rb +38 -0
- data/lib/rdkafka/callbacks/list_consumer_groups_handler.rb +39 -0
- data/lib/rdkafka/callbacks.rb +4 -2
- data/lib/rdkafka/clients.rb +114 -0
- data/lib/rdkafka/config.rb +103 -56
- data/lib/rdkafka/consumer/allocator.rb +38 -0
- data/lib/rdkafka/consumer/topic_partition_list.rb +16 -12
- data/lib/rdkafka/consumer.rb +81 -76
- data/lib/rdkafka/defaults.rb +11 -2
- data/lib/rdkafka/error.rb +2 -1
- data/lib/rdkafka/helpers/oauth.rb +10 -5
- data/lib/rdkafka/native_kafka.rb +20 -7
- data/lib/rdkafka/producer/delivery_handle.rb +1 -2
- data/lib/rdkafka/producer/testing.rb +2 -1
- data/lib/rdkafka/producer.rb +28 -20
- data/lib/rdkafka/version.rb +1 -1
- data/lib/rdkafka.rb +10 -0
- data/package-lock.json +6 -6
- data/renovate.json +1 -1
- metadata +10 -2
data/lib/rdkafka/consumer.rb
CHANGED
|
@@ -35,11 +35,13 @@ module Rdkafka
|
|
|
35
35
|
# consumer-queue reference, then destroy the native client. The default `NativeKafka#finalizer`
|
|
36
36
|
# went straight to `rd_kafka_destroy`, leaving the consumer-queue reference (from
|
|
37
37
|
# `rd_kafka_queue_get_consumer`, taken by `poll_batch`) dangling - which can make
|
|
38
|
-
# `rd_kafka_destroy` block inside the finalizer (process hang at GC/shutdown) or leak the
|
|
38
|
+
# `rd_kafka_destroy` block inside the finalizer (process hang at GC/shutdown) or leak the
|
|
39
|
+
# handle.
|
|
39
40
|
#
|
|
40
41
|
# @private
|
|
41
42
|
# @param native_kafka [NativeKafka] the wrapped native client
|
|
42
|
-
# @param queue_holder [Array] single-element holder carrying the consumer queue pointer
|
|
43
|
+
# @param queue_holder [Array] single-element holder carrying the consumer queue pointer
|
|
44
|
+
# (or empty)
|
|
43
45
|
# @return [Proc] finalizer proc that must not reference the consumer instance
|
|
44
46
|
def self.finalizer(native_kafka, queue_holder)
|
|
45
47
|
proc do
|
|
@@ -125,11 +127,6 @@ module Rdkafka
|
|
|
125
127
|
# @return [nil]
|
|
126
128
|
# @raise [Rdkafka::ClosedConsumerError] if called on a closed consumer
|
|
127
129
|
#
|
|
128
|
-
# @note This method holds the inner lock until the queue is empty or `:stop` is returned.
|
|
129
|
-
# Other consumer operations will wait until this method returns.
|
|
130
|
-
# @note This method is thread-safe as it uses @native_kafka.with_inner synchronization
|
|
131
|
-
# @note Do NOT use this if `consumer_poll_set` was set to `true`
|
|
132
|
-
#
|
|
133
130
|
# @example Drain all pending events
|
|
134
131
|
# consumer.events_poll_nb_each { |_count| }
|
|
135
132
|
#
|
|
@@ -138,6 +135,10 @@ module Rdkafka
|
|
|
138
135
|
# consumer.events_poll_nb_each do |_count|
|
|
139
136
|
# :stop if monotonic_now >= deadline
|
|
140
137
|
# end
|
|
138
|
+
# @note This method holds the inner lock until the queue is empty or `:stop` is returned.
|
|
139
|
+
# Other consumer operations will wait until this method returns.
|
|
140
|
+
# @note This method is thread-safe as it uses @native_kafka.with_inner synchronization
|
|
141
|
+
# @note Do NOT use this if `consumer_poll_set` was set to `true`
|
|
141
142
|
def events_poll_nb_each
|
|
142
143
|
closed_consumer_check(__method__)
|
|
143
144
|
|
|
@@ -163,14 +164,6 @@ module Rdkafka
|
|
|
163
164
|
# @raise [Rdkafka::ClosedConsumerError] if called on a closed consumer
|
|
164
165
|
# @raise [Rdkafka::RdkafkaError] if a Kafka error occurs while polling
|
|
165
166
|
#
|
|
166
|
-
# @note This method uses `rd_kafka_consumer_poll` to fetch messages, unlike
|
|
167
|
-
# `events_poll_nb_each` which uses `rd_kafka_poll` for event callbacks (delivery reports,
|
|
168
|
-
# statistics, etc.). For consumers, use this method to receive messages and
|
|
169
|
-
# `events_poll_nb_each` for processing background events.
|
|
170
|
-
# @note This method holds the inner lock for the duration. Other consumer operations
|
|
171
|
-
# will wait until this method returns.
|
|
172
|
-
# @note Timeout/max_messages logic should be implemented by the caller
|
|
173
|
-
#
|
|
174
167
|
# @example Process messages until queue is empty
|
|
175
168
|
# consumer.poll_nb_each do |message|
|
|
176
169
|
# process(message)
|
|
@@ -183,6 +176,13 @@ module Rdkafka
|
|
|
183
176
|
# count += 1
|
|
184
177
|
# :stop if count >= 10
|
|
185
178
|
# end
|
|
179
|
+
# @note This method uses `rd_kafka_consumer_poll` to fetch messages, unlike
|
|
180
|
+
# `events_poll_nb_each` which uses `rd_kafka_poll` for event callbacks (delivery reports,
|
|
181
|
+
# statistics, etc.). For consumers, use this method to receive messages and
|
|
182
|
+
# `events_poll_nb_each` for processing background events.
|
|
183
|
+
# @note This method holds the inner lock for the duration. Other consumer operations
|
|
184
|
+
# will wait until this method returns.
|
|
185
|
+
# @note Timeout/max_messages logic should be implemented by the caller
|
|
186
186
|
def poll_nb_each
|
|
187
187
|
closed_consumer_check(__method__)
|
|
188
188
|
|
|
@@ -437,9 +437,11 @@ module Rdkafka
|
|
|
437
437
|
end
|
|
438
438
|
|
|
439
439
|
# Return the current positions (offsets) for topics and partitions.
|
|
440
|
-
# The offset field of each requested partition will be set to the offset of the last consumed
|
|
440
|
+
# The offset field of each requested partition will be set to the offset of the last consumed
|
|
441
|
+
# message + 1, or nil in case there was no previous message.
|
|
441
442
|
#
|
|
442
|
-
# @param list [TopicPartitionList, nil] The topic with partitions to get the offsets for or nil
|
|
443
|
+
# @param list [TopicPartitionList, nil] The topic with partitions to get the offsets for or nil
|
|
444
|
+
# to use the current subscription.
|
|
443
445
|
#
|
|
444
446
|
# @return [TopicPartitionList]
|
|
445
447
|
#
|
|
@@ -592,7 +594,7 @@ module Rdkafka
|
|
|
592
594
|
# When using this `enable.auto.offset.store` should be set to `false` in the config.
|
|
593
595
|
#
|
|
594
596
|
# @param message [Rdkafka::Consumer::Message] The message which offset will be stored
|
|
595
|
-
# @param metadata [String, nil] commit metadata string
|
|
597
|
+
# @param metadata [String, nil] commit metadata string to store alongside the offset
|
|
596
598
|
# @return [nil]
|
|
597
599
|
# @raise [RdkafkaError] When storing the offset fails
|
|
598
600
|
def store_offset(message, metadata = nil)
|
|
@@ -646,9 +648,8 @@ module Rdkafka
|
|
|
646
648
|
seek_by(message.topic, message.partition, message.offset)
|
|
647
649
|
end
|
|
648
650
|
|
|
649
|
-
# Seek to a particular message by providing the topic, partition and offset.
|
|
650
|
-
#
|
|
651
|
-
# message at the given offset.
|
|
651
|
+
# Seek to a particular message by providing the topic, partition and offset. The next poll on
|
|
652
|
+
# the topic/partition will return the message at the given offset.
|
|
652
653
|
#
|
|
653
654
|
# @param topic [String] The topic in which to seek
|
|
654
655
|
# @param partition [Integer] The partition number to seek
|
|
@@ -875,7 +876,7 @@ module Rdkafka
|
|
|
875
876
|
# returns without further waiting.
|
|
876
877
|
#
|
|
877
878
|
# Error events (e.g. `:partition_eof`) are returned inline as {RdkafkaError} objects
|
|
878
|
-
# rather than raised, so callers receive the complete batch
|
|
879
|
+
# rather than raised, so callers receive the complete batch - both messages and errors -
|
|
879
880
|
# and can decide how to handle each. This is particularly useful when multiple partitions
|
|
880
881
|
# signal EOF simultaneously: all signals appear in the returned array rather than only
|
|
881
882
|
# the first one being raised and the rest silently discarded.
|
|
@@ -905,33 +906,35 @@ module Rdkafka
|
|
|
905
906
|
begin
|
|
906
907
|
while i < count
|
|
907
908
|
ptr = buffer.get_pointer(i * FFI::Pointer.size)
|
|
909
|
+
# Take ownership of this index before processing the pointer: advancing `i` up front means
|
|
910
|
+
# the cleanup loop in the `ensure` below starts past it, so a raise mid-iteration cannot
|
|
911
|
+
# double-free the same message.
|
|
912
|
+
i += 1
|
|
908
913
|
|
|
909
|
-
if ptr.null?
|
|
910
|
-
i += 1
|
|
911
|
-
next
|
|
912
|
-
end
|
|
914
|
+
next if ptr.null?
|
|
913
915
|
|
|
914
|
-
|
|
916
|
+
# One `ensure` around the whole body guarantees `ptr` is destroyed exactly once on every
|
|
917
|
+
# path - the error branch, a successful build, a rescued `RdkafkaError`, or any other
|
|
918
|
+
# exception unwinding through here - so nothing leaks in the window before the build.
|
|
919
|
+
begin
|
|
920
|
+
native_message = Rdkafka::Bindings::Message.new(ptr)
|
|
915
921
|
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
next
|
|
921
|
-
end
|
|
922
|
+
if native_message[:err] != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
923
|
+
results << build_batch_error(native_message)
|
|
924
|
+
next
|
|
925
|
+
end
|
|
922
926
|
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
927
|
+
begin
|
|
928
|
+
results << Rdkafka::Consumer::Message.new(native_message)
|
|
929
|
+
rescue Rdkafka::RdkafkaError => e
|
|
930
|
+
# A message that fails to build (e.g. a header read error) is surfaced inline as an
|
|
931
|
+
# error event rather than discarding the whole batch - including the messages already
|
|
932
|
+
# built - and raising, which silently lost them once their offsets had been stored.
|
|
933
|
+
results << e
|
|
934
|
+
end
|
|
930
935
|
ensure
|
|
931
936
|
Rdkafka::Bindings.rd_kafka_message_destroy(ptr)
|
|
932
937
|
end
|
|
933
|
-
|
|
934
|
-
i += 1
|
|
935
938
|
end
|
|
936
939
|
ensure
|
|
937
940
|
while i < count
|
|
@@ -950,15 +953,15 @@ module Rdkafka
|
|
|
950
953
|
# particularly useful in fiber scheduler contexts where GVL release/reacquire
|
|
951
954
|
# overhead is wasteful since we don't expect to wait.
|
|
952
955
|
#
|
|
956
|
+
# @param timeout_ms [Integer] Timeout waiting for the first message
|
|
957
|
+
# (default: 0 for non-blocking)
|
|
958
|
+
# @param max_items [Integer] Maximum number of messages to return per call
|
|
959
|
+
# @return [Array<Message, RdkafkaError>] Batch of messages and/or error events in arrival order
|
|
960
|
+
# @raise [ClosedConsumerError] When called on a closed consumer
|
|
953
961
|
# @note Since the GVL is not released, a non-zero timeout_ms will block all Ruby
|
|
954
962
|
# threads/fibers for the duration. Use {#poll_batch} if you need a blocking wait.
|
|
955
963
|
#
|
|
956
964
|
# Error events are returned inline as {RdkafkaError} objects; see {#poll_batch} for details.
|
|
957
|
-
#
|
|
958
|
-
# @param timeout_ms [Integer] Timeout waiting for the first message (default: 0 for non-blocking)
|
|
959
|
-
# @param max_items [Integer] Maximum number of messages to return per call
|
|
960
|
-
# @return [Array<Message, RdkafkaError>] Batch of messages and/or error events in arrival order
|
|
961
|
-
# @raise [ClosedConsumerError] When called on a closed consumer
|
|
962
965
|
def poll_batch_nb(timeout_ms = 0, max_items: 100)
|
|
963
966
|
closed_consumer_check(__method__)
|
|
964
967
|
|
|
@@ -980,33 +983,35 @@ module Rdkafka
|
|
|
980
983
|
begin
|
|
981
984
|
while i < count
|
|
982
985
|
ptr = buffer.get_pointer(i * FFI::Pointer.size)
|
|
986
|
+
# Take ownership of this index before processing the pointer: advancing `i` up front means
|
|
987
|
+
# the cleanup loop in the `ensure` below starts past it, so a raise mid-iteration cannot
|
|
988
|
+
# double-free the same message.
|
|
989
|
+
i += 1
|
|
983
990
|
|
|
984
|
-
if ptr.null?
|
|
985
|
-
i += 1
|
|
986
|
-
next
|
|
987
|
-
end
|
|
991
|
+
next if ptr.null?
|
|
988
992
|
|
|
989
|
-
|
|
993
|
+
# One `ensure` around the whole body guarantees `ptr` is destroyed exactly once on every
|
|
994
|
+
# path - the error branch, a successful build, a rescued `RdkafkaError`, or any other
|
|
995
|
+
# exception unwinding through here - so nothing leaks in the window before the build.
|
|
996
|
+
begin
|
|
997
|
+
native_message = Rdkafka::Bindings::Message.new(ptr)
|
|
990
998
|
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
next
|
|
996
|
-
end
|
|
999
|
+
if native_message[:err] != Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
1000
|
+
results << build_batch_error(native_message)
|
|
1001
|
+
next
|
|
1002
|
+
end
|
|
997
1003
|
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1004
|
+
begin
|
|
1005
|
+
results << Rdkafka::Consumer::Message.new(native_message)
|
|
1006
|
+
rescue Rdkafka::RdkafkaError => e
|
|
1007
|
+
# A message that fails to build (e.g. a header read error) is surfaced inline as an
|
|
1008
|
+
# error event rather than discarding the whole batch - including the messages already
|
|
1009
|
+
# built - and raising, which silently lost them once their offsets had been stored.
|
|
1010
|
+
results << e
|
|
1011
|
+
end
|
|
1005
1012
|
ensure
|
|
1006
1013
|
Rdkafka::Bindings.rd_kafka_message_destroy(ptr)
|
|
1007
1014
|
end
|
|
1008
|
-
|
|
1009
|
-
i += 1
|
|
1010
1015
|
end
|
|
1011
1016
|
ensure
|
|
1012
1017
|
while i < count
|
|
@@ -1085,7 +1090,15 @@ module Rdkafka
|
|
|
1085
1090
|
|
|
1086
1091
|
private
|
|
1087
1092
|
|
|
1088
|
-
#
|
|
1093
|
+
# Checks if the consumer is closed and raises an error if so
|
|
1094
|
+
# @param method [Symbol] name of the calling method for error context
|
|
1095
|
+
# @raise [ClosedConsumerError] when the consumer is closed
|
|
1096
|
+
def closed_consumer_check(method)
|
|
1097
|
+
raise Rdkafka::ClosedConsumerError.new(method) if closed?
|
|
1098
|
+
end
|
|
1099
|
+
alias_method :closed_check, :closed_consumer_check
|
|
1100
|
+
|
|
1101
|
+
# Reads a librdkafka-allocated string and frees the underlying native buffer.
|
|
1089
1102
|
#
|
|
1090
1103
|
# `rd_kafka_memberid`/`rd_kafka_clusterid` return a string the caller owns and must release
|
|
1091
1104
|
# with `rd_kafka_mem_free`; without this the buffer leaks on every call.
|
|
@@ -1101,14 +1114,6 @@ module Rdkafka
|
|
|
1101
1114
|
Rdkafka::Bindings.rd_kafka_mem_free(inner, ptr) unless ptr.null?
|
|
1102
1115
|
end
|
|
1103
1116
|
|
|
1104
|
-
# Checks if the consumer is closed and raises an error if so
|
|
1105
|
-
# @param method [Symbol] name of the calling method for error context
|
|
1106
|
-
# @raise [ClosedConsumerError] when the consumer is closed
|
|
1107
|
-
def closed_consumer_check(method)
|
|
1108
|
-
raise Rdkafka::ClosedConsumerError.new(method) if closed?
|
|
1109
|
-
end
|
|
1110
|
-
alias_method :closed_check, :closed_consumer_check
|
|
1111
|
-
|
|
1112
1117
|
# Reads this consumer's effective `isolation.level` from the live librdkafka configuration
|
|
1113
1118
|
# and maps it to the numeric isolation level constant. Memoized: the value cannot change
|
|
1114
1119
|
# after client creation.
|
data/lib/rdkafka/defaults.rb
CHANGED
|
@@ -3,8 +3,17 @@
|
|
|
3
3
|
module Rdkafka
|
|
4
4
|
# Provides default timeout and configuration values used throughout the library.
|
|
5
5
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
6
|
+
# All timeout values can be overridden per-call via method parameters. These constants provide
|
|
7
|
+
# a central place to understand and reference the default values used across the library.
|
|
8
|
+
#
|
|
9
|
+
# @example Overriding a timeout per-call
|
|
10
|
+
# consumer.committed(timeout_ms: 5_000) # Use 5 seconds instead of default 2 seconds
|
|
11
|
+
#
|
|
12
|
+
# @example Checking the default value
|
|
13
|
+
# Rdkafka::Defaults::CONSUMER_COMMITTED_TIMEOUT_MS # => 2000
|
|
14
|
+
# @note These are rdkafka-ruby defaults, not librdkafka configuration options.
|
|
15
|
+
# For librdkafka options, see:
|
|
16
|
+
# https://github.com/confluentinc/librdkafka/blob/master/CONFIGURATION.md
|
|
8
17
|
module Defaults
|
|
9
18
|
# Consumer timeout for fetching committed offsets
|
|
10
19
|
# @see Consumer#committed
|
data/lib/rdkafka/error.rb
CHANGED
|
@@ -139,7 +139,8 @@ module Rdkafka
|
|
|
139
139
|
# Calls rd_kafka_fatal_error() to get the actual underlying error code and description.
|
|
140
140
|
#
|
|
141
141
|
# @param client_ptr [FFI::Pointer] Pointer to rd_kafka_t client
|
|
142
|
-
# @param fallback_error_code [Integer] Error code to use if no fatal error found
|
|
142
|
+
# @param fallback_error_code [Integer] Error code to use if no fatal error found
|
|
143
|
+
# (default: -150)
|
|
143
144
|
# @param fallback_message [String, nil] Message to use if no fatal error found
|
|
144
145
|
# @param instance_name [String, nil] Optional name of the rdkafka instance
|
|
145
146
|
# @return [RdkafkaError] Error object with fatal flag set to true
|
|
@@ -4,10 +4,14 @@ module Rdkafka
|
|
|
4
4
|
module OAuth
|
|
5
5
|
# Set the OAuthBearer token
|
|
6
6
|
#
|
|
7
|
-
# @param token [String] the mandatory token value to set, often (but not necessarily) a JWS
|
|
8
|
-
#
|
|
7
|
+
# @param token [String] the mandatory token value to set, often (but not necessarily) a JWS
|
|
8
|
+
# compact serialization as per https://tools.ietf.org/html/rfc7515#section-3.1.
|
|
9
|
+
# @param lifetime_ms [Integer] when the token expires, in terms of the number of milliseconds
|
|
10
|
+
# since the epoch. See https://currentmillis.com/.
|
|
9
11
|
# @param principal_name [String] the mandatory Kafka principal name associated with the token.
|
|
10
|
-
# @param extensions [Hash] optional SASL extensions key-value pairs to be communicated to the
|
|
12
|
+
# @param extensions [Hash] optional SASL extensions key-value pairs to be communicated to the
|
|
13
|
+
# broker as additional key-value pairs during the initial client response as per
|
|
14
|
+
# https://tools.ietf.org/html/rfc7628#section-3.1.
|
|
11
15
|
# @return [Integer] 0 on success
|
|
12
16
|
def oauthbearer_set_token(token:, lifetime_ms:, principal_name:, extensions: nil)
|
|
13
17
|
error_buffer = FFI::MemoryPointer.from_string(" " * 256)
|
|
@@ -49,7 +53,8 @@ module Rdkafka
|
|
|
49
53
|
# Convert extensions hash to FFI::MemoryPointer (`const char **`).
|
|
50
54
|
#
|
|
51
55
|
# @param extensions [Hash, nil] extension key-value pairs
|
|
52
|
-
# @return [Array<FFI::MemoryPointer, Array<FFI::MemoryPointer>>] array pointer and string
|
|
56
|
+
# @return [Array<FFI::MemoryPointer, Array<FFI::MemoryPointer>>] array pointer and string
|
|
57
|
+
# pointers
|
|
53
58
|
# @note The returned pointers must be freed manually (autorelease = false).
|
|
54
59
|
def map_extensions(extensions)
|
|
55
60
|
return [nil, nil] if extensions.nil? || extensions.empty?
|
|
@@ -80,7 +85,7 @@ module Rdkafka
|
|
|
80
85
|
#
|
|
81
86
|
# @param extensions [Hash, nil] extension key-value pairs
|
|
82
87
|
# @return [Integer] non-negative even number representing keys + values count
|
|
83
|
-
# @see
|
|
88
|
+
# @see github.com/confluentinc/librdkafka/blob/master/src/rdkafka_sasl_oauthbearer.c#L327-L347
|
|
84
89
|
def extension_size(extensions)
|
|
85
90
|
return 0 unless extensions
|
|
86
91
|
extensions.size * 2
|
data/lib/rdkafka/native_kafka.rb
CHANGED
|
@@ -13,6 +13,14 @@ module Rdkafka
|
|
|
13
13
|
def initialize(inner, run_polling_thread:, opaque:, auto_start: true, timeout_ms: Defaults::NATIVE_KAFKA_POLL_TIMEOUT_MS)
|
|
14
14
|
@inner = inner
|
|
15
15
|
@opaque = opaque
|
|
16
|
+
# Process that owns `@inner`. librdkafka is not fork-safe: `fork` copies only the calling
|
|
17
|
+
# thread, so the background/broker threads backing this handle do not exist in a child
|
|
18
|
+
# process. An inherited handle therefore must not be polled or destroyed in the child -
|
|
19
|
+
# `rd_kafka_destroy` would walk thread state that no longer exists (segfault) and the
|
|
20
|
+
# inherited mutexes may have been copied in a locked state (deadlock). We record the creator
|
|
21
|
+
# pid so a forked child can recognise an inherited handle and leave its teardown to the
|
|
22
|
+
# parent, which still owns the running threads.
|
|
23
|
+
@creator_pid = Process.pid
|
|
16
24
|
# Lock around external access
|
|
17
25
|
@access_mutex = Mutex.new
|
|
18
26
|
# Lock around internal polling
|
|
@@ -116,17 +124,21 @@ module Rdkafka
|
|
|
116
124
|
end
|
|
117
125
|
|
|
118
126
|
# Returns whether this native Kafka handle is closed or closing
|
|
127
|
+
#
|
|
128
|
+
# A handle inherited across `fork` is reported as closed in the child: it belongs to another
|
|
129
|
+
# process whose threads back the native client, so it is not usable here and must not be
|
|
130
|
+
# destroyed here (see the `@creator_pid` note in `#initialize`). This makes every `#close`
|
|
131
|
+
# path - including the GC finalizers that run during a child's exit - skip the native teardown
|
|
132
|
+
# for inherited handles, which is what would otherwise segfault the child.
|
|
133
|
+
#
|
|
119
134
|
# @return [Boolean] true if closed or closing
|
|
120
135
|
def closed?
|
|
121
|
-
@closing || @inner.nil?
|
|
136
|
+
@closing || @inner.nil? || @creator_pid != Process.pid
|
|
122
137
|
end
|
|
123
138
|
|
|
124
139
|
# Enable IO event notifications on the main queue
|
|
125
140
|
# Librdkafka will write to your FD when the queue transitions from empty to non-empty
|
|
126
141
|
#
|
|
127
|
-
# @note This method is incompatible with background polling threads.
|
|
128
|
-
# If background polling is enabled, use manual polling instead (e.g., consumer.poll)
|
|
129
|
-
#
|
|
130
142
|
# @param fd [Integer] your file descriptor (from IO.pipe or eventfd)
|
|
131
143
|
# @param payload [String] data to write to fd when queue has data (default: "\x01")
|
|
132
144
|
# @return [nil]
|
|
@@ -143,6 +155,8 @@ module Rdkafka
|
|
|
143
155
|
# if readable
|
|
144
156
|
# consumer.poll(0) # Get messages
|
|
145
157
|
# end
|
|
158
|
+
# @note This method is incompatible with background polling threads.
|
|
159
|
+
# If background polling is enabled, use manual polling instead (e.g., consumer.poll)
|
|
146
160
|
def enable_main_queue_io_events(fd, payload = "\x01")
|
|
147
161
|
if @run_polling_thread
|
|
148
162
|
raise "Cannot enable IO events while background polling thread is active. " \
|
|
@@ -160,14 +174,13 @@ module Rdkafka
|
|
|
160
174
|
# Enable IO event notifications on the background queue
|
|
161
175
|
# Librdkafka will write to your FD when the background queue transitions from empty to non-empty
|
|
162
176
|
#
|
|
163
|
-
# @note This method is incompatible with background polling threads.
|
|
164
|
-
# If background polling is enabled, use manual polling instead (e.g., consumer.poll)
|
|
165
|
-
#
|
|
166
177
|
# @param fd [Integer] your file descriptor (from IO.pipe or eventfd)
|
|
167
178
|
# @param payload [String] data to write to fd when queue has data (default: "\x01")
|
|
168
179
|
# @return [nil]
|
|
169
180
|
# @raise [ClosedInnerError] when the handle is closed
|
|
170
181
|
# @raise [RuntimeError] when background polling thread is active
|
|
182
|
+
# @note This method is incompatible with background polling threads.
|
|
183
|
+
# If background polling is enabled, use manual polling instead (e.g., consumer.poll)
|
|
171
184
|
def enable_background_queue_io_events(fd, payload = "\x01")
|
|
172
185
|
if @run_polling_thread
|
|
173
186
|
raise "Cannot enable IO events while background polling thread is active. " \
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Rdkafka
|
|
4
4
|
class Producer
|
|
5
|
-
# Handle to wait for a delivery report which is returned when
|
|
6
|
-
# producing a message.
|
|
5
|
+
# Handle to wait for a delivery report which is returned when producing a message.
|
|
7
6
|
class DeliveryHandle < Rdkafka::AbstractHandle
|
|
8
7
|
layout :pending, :bool,
|
|
9
8
|
:response, :int,
|
|
@@ -36,7 +36,8 @@ module Rdkafka
|
|
|
36
36
|
# Checks if a fatal error has occurred and retrieves error details.
|
|
37
37
|
# Calls rd_kafka_fatal_error to get the actual fatal error code and message.
|
|
38
38
|
#
|
|
39
|
-
# @return [Hash, nil] Hash with :error_code and :error_string if fatal error occurred,
|
|
39
|
+
# @return [Hash, nil] Hash with :error_code and :error_string if fatal error occurred,
|
|
40
|
+
# nil otherwise
|
|
40
41
|
#
|
|
41
42
|
# @example
|
|
42
43
|
# if fatal_error = producer.fatal_error
|
data/lib/rdkafka/producer.rb
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Rdkafka
|
|
4
|
-
# A producer for Kafka messages. To create a producer set up a {Config} and call
|
|
4
|
+
# A producer for Kafka messages. To create a producer set up a {Config} and call
|
|
5
|
+
# {Config#producer producer} on that.
|
|
5
6
|
class Producer
|
|
6
7
|
include Helpers::Time
|
|
7
8
|
include Helpers::OAuth
|
|
8
9
|
include Helpers::Metadata
|
|
9
10
|
|
|
10
|
-
#
|
|
11
|
+
# @!visibility private
|
|
11
12
|
@@partitions_count_cache = PartitionsCountCache.new
|
|
12
13
|
|
|
13
14
|
# Global (process wide) partitions cache. We use it to store number of topics partitions,
|
|
@@ -175,8 +176,7 @@ module Rdkafka
|
|
|
175
176
|
end
|
|
176
177
|
end
|
|
177
178
|
|
|
178
|
-
# Begin a new transaction
|
|
179
|
-
# Requires {#init_transactions} to have been called first
|
|
179
|
+
# Begin a new transaction. Requires {#init_transactions} to have been called first
|
|
180
180
|
#
|
|
181
181
|
# @return [true] Returns true on success
|
|
182
182
|
# @raise [RdkafkaError] if beginning the transaction fails
|
|
@@ -331,10 +331,9 @@ module Rdkafka
|
|
|
331
331
|
# @return [Integer] the number of messages in the queue
|
|
332
332
|
# @raise [Rdkafka::ClosedProducerError] if called on a closed producer
|
|
333
333
|
#
|
|
334
|
-
# @note This method is thread-safe as it uses the @native_kafka.with_inner synchronization
|
|
335
|
-
#
|
|
336
334
|
# @example
|
|
337
335
|
# producer.queue_size #=> 42
|
|
336
|
+
# @note This method is thread-safe as it uses the @native_kafka.with_inner synchronization
|
|
338
337
|
def queue_size
|
|
339
338
|
closed_producer_check(__method__)
|
|
340
339
|
|
|
@@ -360,10 +359,6 @@ module Rdkafka
|
|
|
360
359
|
# @return [nil]
|
|
361
360
|
# @raise [Rdkafka::ClosedProducerError] if called on a closed producer
|
|
362
361
|
#
|
|
363
|
-
# @note This method holds the inner lock until the queue is empty or `:stop` is returned.
|
|
364
|
-
# Other producer operations (produce, close, etc.) will wait until this method returns.
|
|
365
|
-
# @note This method is thread-safe as it uses @native_kafka.with_inner synchronization
|
|
366
|
-
#
|
|
367
362
|
# @example Drain all pending callbacks
|
|
368
363
|
# producer.events_poll_nb_each { |_count| }
|
|
369
364
|
#
|
|
@@ -372,6 +367,9 @@ module Rdkafka
|
|
|
372
367
|
# producer.events_poll_nb_each do |_count|
|
|
373
368
|
# :stop if monotonic_now >= deadline
|
|
374
369
|
# end
|
|
370
|
+
# @note This method holds the inner lock until the queue is empty or `:stop` is returned.
|
|
371
|
+
# Other producer operations (produce, close, etc.) will wait until this method returns.
|
|
372
|
+
# @note This method is thread-safe as it uses @native_kafka.with_inner synchronization
|
|
375
373
|
def events_poll_nb_each
|
|
376
374
|
closed_producer_check(__method__)
|
|
377
375
|
|
|
@@ -387,7 +385,8 @@ module Rdkafka
|
|
|
387
385
|
# Partition count for a given topic.
|
|
388
386
|
#
|
|
389
387
|
# @param topic [String] The topic name.
|
|
390
|
-
# @return [Integer] partition count for a given topic or `RD_KAFKA_PARTITION_UA (-1)` if it
|
|
388
|
+
# @return [Integer] partition count for a given topic or `RD_KAFKA_PARTITION_UA (-1)` if it
|
|
389
|
+
# could not be obtained.
|
|
391
390
|
#
|
|
392
391
|
# @note If 'allow.auto.create.topics' is set to true in the broker, the topic will be
|
|
393
392
|
# auto-created after returning nil.
|
|
@@ -422,23 +421,32 @@ module Rdkafka
|
|
|
422
421
|
end
|
|
423
422
|
end
|
|
424
423
|
|
|
425
|
-
# Produces a message to a Kafka topic. The message is added to rdkafka's queue, call
|
|
424
|
+
# Produces a message to a Kafka topic. The message is added to rdkafka's queue, call
|
|
425
|
+
# {DeliveryHandle#wait wait} on the returned delivery handle to make sure it is delivered.
|
|
426
426
|
#
|
|
427
|
-
# When no partition is specified the underlying Kafka library picks a partition based on the
|
|
428
|
-
#
|
|
427
|
+
# When no partition is specified the underlying Kafka library picks a partition based on the
|
|
428
|
+
# key. If no key is specified, a random partition will be used. When a timestamp is provided
|
|
429
|
+
# this is used instead of the auto-generated timestamp.
|
|
429
430
|
#
|
|
430
431
|
# @param topic [String] The topic to produce to
|
|
431
432
|
# @param payload [String, nil]
|
|
432
433
|
# @param key [String, nil]
|
|
433
434
|
# @param partition [Integer, nil] Optional partition to produce to
|
|
434
|
-
# @param partition_key [String, nil] Optional partition key based on which partition
|
|
435
|
-
#
|
|
436
|
-
# @param
|
|
437
|
-
#
|
|
438
|
-
# @param
|
|
435
|
+
# @param partition_key [String, nil] Optional partition key based on which partition
|
|
436
|
+
# assignment can happen
|
|
437
|
+
# @param timestamp [Time, Integer, nil] Optional timestamp of this message. Integer
|
|
438
|
+
# timestamp is in milliseconds since Jan 1 1970.
|
|
439
|
+
# @param headers [Hash{String => String, Array<String>}] Optional message headers. Values
|
|
440
|
+
# can be either a single string or an array of strings to support duplicate headers per
|
|
441
|
+
# KIP-82
|
|
442
|
+
# @param label [Object, nil] a label that can be assigned when producing a message that
|
|
443
|
+
# will be part of the delivery handle and the delivery report
|
|
444
|
+
# @param topic_config [Hash] topic config for given message dispatch. Allows to send
|
|
445
|
+
# messages to topics with different configuration
|
|
439
446
|
# @param partitioner [String] name of the partitioner to use
|
|
440
447
|
#
|
|
441
|
-
# @return [DeliveryHandle] Delivery handle that can be used to wait for the result of
|
|
448
|
+
# @return [DeliveryHandle] Delivery handle that can be used to wait for the result of
|
|
449
|
+
# producing this message
|
|
442
450
|
#
|
|
443
451
|
# @raise [RdkafkaError] When adding the message to rdkafka's queue failed
|
|
444
452
|
def produce(
|
data/lib/rdkafka/version.rb
CHANGED
data/lib/rdkafka.rb
CHANGED
|
@@ -17,6 +17,8 @@ require "rdkafka/admin/create_topic_handle"
|
|
|
17
17
|
require "rdkafka/admin/create_topic_report"
|
|
18
18
|
require "rdkafka/admin/delete_groups_handle"
|
|
19
19
|
require "rdkafka/admin/delete_groups_report"
|
|
20
|
+
require "rdkafka/admin/delete_records_handle"
|
|
21
|
+
require "rdkafka/admin/delete_records_report"
|
|
20
22
|
require "rdkafka/admin/delete_topic_handle"
|
|
21
23
|
require "rdkafka/admin/delete_topic_report"
|
|
22
24
|
require "rdkafka/admin/create_partitions_handle"
|
|
@@ -33,6 +35,8 @@ require "rdkafka/admin/incremental_alter_configs_handle"
|
|
|
33
35
|
require "rdkafka/admin/incremental_alter_configs_report"
|
|
34
36
|
require "rdkafka/admin/list_offsets_handle"
|
|
35
37
|
require "rdkafka/admin/list_offsets_report"
|
|
38
|
+
require "rdkafka/admin/list_consumer_groups_handle"
|
|
39
|
+
require "rdkafka/admin/list_consumer_groups_report"
|
|
36
40
|
require "rdkafka/admin/acl_binding_result"
|
|
37
41
|
require "rdkafka/admin/config_binding_result"
|
|
38
42
|
require "rdkafka/admin/config_resource_binding_result"
|
|
@@ -43,17 +47,20 @@ require "rdkafka/callbacks/create_topic_handler"
|
|
|
43
47
|
require "rdkafka/callbacks/delete_topic_handler"
|
|
44
48
|
require "rdkafka/callbacks/create_partitions_handler"
|
|
45
49
|
require "rdkafka/callbacks/delete_groups_handler"
|
|
50
|
+
require "rdkafka/callbacks/delete_records_handler"
|
|
46
51
|
require "rdkafka/callbacks/create_acl_handler"
|
|
47
52
|
require "rdkafka/callbacks/delete_acl_handler"
|
|
48
53
|
require "rdkafka/callbacks/describe_acl_handler"
|
|
49
54
|
require "rdkafka/callbacks/describe_configs_handler"
|
|
50
55
|
require "rdkafka/callbacks/incremental_alter_configs_handler"
|
|
51
56
|
require "rdkafka/callbacks/list_offsets_handler"
|
|
57
|
+
require "rdkafka/callbacks/list_consumer_groups_handler"
|
|
52
58
|
require "rdkafka/config"
|
|
53
59
|
require "rdkafka/consumer"
|
|
54
60
|
require "rdkafka/consumer/headers"
|
|
55
61
|
require "rdkafka/consumer/message"
|
|
56
62
|
require "rdkafka/consumer/partition"
|
|
63
|
+
require "rdkafka/consumer/allocator"
|
|
57
64
|
require "rdkafka/consumer/topic_partition_list"
|
|
58
65
|
require "rdkafka/error"
|
|
59
66
|
require "rdkafka/metadata"
|
|
@@ -62,9 +69,12 @@ require "rdkafka/producer/partitions_count_cache"
|
|
|
62
69
|
require "rdkafka/producer"
|
|
63
70
|
require "rdkafka/producer/delivery_handle"
|
|
64
71
|
require "rdkafka/producer/delivery_report"
|
|
72
|
+
require "rdkafka/clients"
|
|
65
73
|
|
|
66
74
|
# Main Rdkafka namespace of this gem
|
|
67
75
|
module Rdkafka
|
|
68
76
|
end
|
|
69
77
|
|
|
70
78
|
Rdkafka::Bindings.rd_kafka_global_init
|
|
79
|
+
|
|
80
|
+
at_exit { Rdkafka::Clients.close_all }
|
data/package-lock.json
CHANGED
|
@@ -103,9 +103,9 @@
|
|
|
103
103
|
}
|
|
104
104
|
},
|
|
105
105
|
"node_modules/fastq": {
|
|
106
|
-
"version": "1.20.
|
|
107
|
-
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.
|
|
108
|
-
"integrity": "sha512-
|
|
106
|
+
"version": "1.20.2",
|
|
107
|
+
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.2.tgz",
|
|
108
|
+
"integrity": "sha512-UpGiiODyCGprM8EPP6JodP6jC9Rws6TCuiDOD+nn0CJhR8guI3g/ozo4ugL0vJ+Yz1UtJuuRPqvQuybVOF1VQA==",
|
|
109
109
|
"dev": true,
|
|
110
110
|
"license": "ISC",
|
|
111
111
|
"dependencies": {
|
|
@@ -286,9 +286,9 @@
|
|
|
286
286
|
}
|
|
287
287
|
},
|
|
288
288
|
"node_modules/smol-toml": {
|
|
289
|
-
"version": "1.
|
|
290
|
-
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.
|
|
291
|
-
"integrity": "sha512-
|
|
289
|
+
"version": "1.8.0",
|
|
290
|
+
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.8.0.tgz",
|
|
291
|
+
"integrity": "sha512-kCZr2V3ch9i00x8zXRhjUNVcjG9ijES5dDudkXvUVCT5QlJNQWElSJdZqyPemffHoLNUYwOcou0Fy+ojN0uHSQ==",
|
|
292
292
|
"dev": true,
|
|
293
293
|
"license": "BSD-3-Clause",
|
|
294
294
|
"engines": {
|
data/renovate.json
CHANGED