karafka-rdkafka 0.13.2 → 0.13.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.github/workflows/ci.yml +9 -4
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +54 -26
- data/{LICENSE → MIT-LICENSE} +2 -1
- data/README.md +19 -20
- data/certs/cert_chain.pem +21 -21
- data/docker-compose.yml +16 -15
- data/ext/README.md +1 -1
- data/ext/Rakefile +1 -1
- data/karafka-rdkafka.gemspec +2 -2
- data/lib/rdkafka/abstract_handle.rb +41 -27
- data/lib/rdkafka/admin/create_partitions_handle.rb +6 -3
- data/lib/rdkafka/admin/create_topic_handle.rb +6 -3
- data/lib/rdkafka/admin/delete_topic_handle.rb +6 -3
- data/lib/rdkafka/admin.rb +6 -7
- data/lib/rdkafka/bindings.rb +24 -6
- data/lib/rdkafka/config.rb +53 -19
- data/lib/rdkafka/consumer/headers.rb +2 -4
- data/lib/rdkafka/consumer.rb +119 -93
- data/lib/rdkafka/error.rb +60 -1
- data/lib/rdkafka/helpers/time.rb +14 -0
- data/lib/rdkafka/metadata.rb +4 -4
- data/lib/rdkafka/native_kafka.rb +6 -1
- data/lib/rdkafka/producer/delivery_handle.rb +16 -1
- data/lib/rdkafka/producer/delivery_report.rb +3 -2
- data/lib/rdkafka/producer.rb +89 -17
- data/lib/rdkafka/version.rb +3 -3
- data/lib/rdkafka.rb +10 -1
- data/renovate.json +6 -0
- data/spec/rdkafka/abstract_handle_spec.rb +0 -2
- data/spec/rdkafka/admin/create_topic_handle_spec.rb +4 -4
- data/spec/rdkafka/admin/create_topic_report_spec.rb +0 -2
- data/spec/rdkafka/admin/delete_topic_handle_spec.rb +3 -3
- data/spec/rdkafka/admin/delete_topic_report_spec.rb +0 -2
- data/spec/rdkafka/admin_spec.rb +1 -2
- data/spec/rdkafka/bindings_spec.rb +0 -1
- data/spec/rdkafka/callbacks_spec.rb +0 -2
- data/spec/rdkafka/config_spec.rb +8 -2
- data/spec/rdkafka/consumer/headers_spec.rb +0 -2
- data/spec/rdkafka/consumer/message_spec.rb +0 -2
- data/spec/rdkafka/consumer/partition_spec.rb +0 -2
- data/spec/rdkafka/consumer/topic_partition_list_spec.rb +0 -2
- data/spec/rdkafka/consumer_spec.rb +122 -38
- data/spec/rdkafka/error_spec.rb +0 -2
- data/spec/rdkafka/metadata_spec.rb +2 -3
- data/spec/rdkafka/native_kafka_spec.rb +2 -3
- data/spec/rdkafka/producer/delivery_handle_spec.rb +15 -2
- data/spec/rdkafka/producer/delivery_report_spec.rb +0 -2
- data/spec/rdkafka/producer_spec.rb +293 -1
- data/spec/spec_helper.rb +7 -1
- data.tar.gz.sig +0 -0
- metadata +31 -28
- metadata.gz.sig +0 -0
- data/certs/karafka-pro.pem +0 -11
data/lib/rdkafka/admin.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "objspace"
|
4
|
-
|
5
3
|
module Rdkafka
|
6
4
|
class Admin
|
7
5
|
# @private
|
@@ -30,11 +28,12 @@ module Rdkafka
|
|
30
28
|
|
31
29
|
# Create a topic with the given partition count and replication factor
|
32
30
|
#
|
31
|
+
# @return [CreateTopicHandle] Create topic handle that can be used to wait for the result of
|
32
|
+
# creating the topic
|
33
|
+
#
|
33
34
|
# @raise [ConfigError] When the partition count or replication factor are out of valid range
|
34
35
|
# @raise [RdkafkaError] When the topic name is invalid or the topic already exists
|
35
36
|
# @raise [RdkafkaError] When the topic configuration is invalid
|
36
|
-
#
|
37
|
-
# @return [CreateTopicHandle] Create topic handle that can be used to wait for the result of creating the topic
|
38
37
|
def create_topic(topic_name, partition_count, replication_factor, topic_config={})
|
39
38
|
closed_admin_check(__method__)
|
40
39
|
|
@@ -107,11 +106,11 @@ module Rdkafka
|
|
107
106
|
create_topic_handle
|
108
107
|
end
|
109
108
|
|
110
|
-
#
|
109
|
+
# Deletes the named topic
|
111
110
|
#
|
111
|
+
# @return [DeleteTopicHandle] Delete topic handle that can be used to wait for the result of
|
112
|
+
# deleting the topic
|
112
113
|
# @raise [RdkafkaError] When the topic name is invalid or the topic does not exist
|
113
|
-
#
|
114
|
-
# @return [DeleteTopicHandle] Delete topic handle that can be used to wait for the result of deleting the topic
|
115
114
|
def delete_topic(topic_name)
|
116
115
|
closed_admin_check(__method__)
|
117
116
|
|
data/lib/rdkafka/bindings.rb
CHANGED
@@ -1,11 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "ffi"
|
4
|
-
require "json"
|
5
|
-
require "logger"
|
6
|
-
|
7
3
|
module Rdkafka
|
8
4
|
# @private
|
5
|
+
#
|
6
|
+
# @note
|
7
|
+
# There are two types of responses related to errors:
|
8
|
+
# - rd_kafka_error_t - a C object that we need to remap into an error or null when no error
|
9
|
+
# - rd_kafka_resp_err_t - response error code (numeric) that we can use directly
|
10
|
+
#
|
11
|
+
# It is critical to ensure, that we handle them correctly. The result type should be:
|
12
|
+
# - rd_kafka_error_t - :pointer
|
13
|
+
# - rd_kafka_resp_err_t - :int
|
9
14
|
module Bindings
|
10
15
|
extend FFI::Library
|
11
16
|
|
@@ -35,7 +40,7 @@ module Rdkafka
|
|
35
40
|
|
36
41
|
# Polling
|
37
42
|
|
38
|
-
attach_function :rd_kafka_flush, [:pointer, :int], :
|
43
|
+
attach_function :rd_kafka_flush, [:pointer, :int], :int, blocking: true
|
39
44
|
attach_function :rd_kafka_poll, [:pointer, :int], :void, blocking: true
|
40
45
|
attach_function :rd_kafka_outq_len, [:pointer], :int, blocking: true
|
41
46
|
|
@@ -96,6 +101,11 @@ module Rdkafka
|
|
96
101
|
|
97
102
|
attach_function :rd_kafka_err2name, [:int], :string
|
98
103
|
attach_function :rd_kafka_err2str, [:int], :string
|
104
|
+
attach_function :rd_kafka_error_is_fatal, [:pointer], :int
|
105
|
+
attach_function :rd_kafka_error_is_retriable, [:pointer], :int
|
106
|
+
attach_function :rd_kafka_error_txn_requires_abort, [:pointer], :int
|
107
|
+
attach_function :rd_kafka_error_destroy, [:pointer], :void
|
108
|
+
attach_function :rd_kafka_error_code, [:pointer], :int
|
99
109
|
|
100
110
|
# Configuration
|
101
111
|
|
@@ -157,7 +167,7 @@ module Rdkafka
|
|
157
167
|
:void, [:pointer, :int, :string, :pointer]
|
158
168
|
) do |_client_prr, err_code, reason, _opaque|
|
159
169
|
if Rdkafka::Config.error_callback
|
160
|
-
error = Rdkafka::RdkafkaError.
|
170
|
+
error = Rdkafka::RdkafkaError.build(err_code, broker_message: reason)
|
161
171
|
error.set_backtrace(caller)
|
162
172
|
Rdkafka::Config.error_callback.call(error)
|
163
173
|
end
|
@@ -194,6 +204,7 @@ module Rdkafka
|
|
194
204
|
attach_function :rd_kafka_resume_partitions, [:pointer, :pointer], :int, blocking: true
|
195
205
|
attach_function :rd_kafka_seek, [:pointer, :int32, :int64, :int], :int, blocking: true
|
196
206
|
attach_function :rd_kafka_offsets_for_times, [:pointer, :pointer, :int], :int, blocking: true
|
207
|
+
attach_function :rd_kafka_position, [:pointer, :pointer], :int, blocking: true
|
197
208
|
|
198
209
|
# Headers
|
199
210
|
attach_function :rd_kafka_header_get_all, [:pointer, :size_t, :pointer, :pointer, SizePtr], :int
|
@@ -255,12 +266,19 @@ module Rdkafka
|
|
255
266
|
RD_KAFKA_VTYPE_TIMESTAMP = 8
|
256
267
|
RD_KAFKA_VTYPE_HEADER = 9
|
257
268
|
RD_KAFKA_VTYPE_HEADERS = 10
|
269
|
+
RD_KAFKA_PURGE_F_QUEUE = 1
|
270
|
+
RD_KAFKA_PURGE_F_INFLIGHT = 2
|
258
271
|
|
259
272
|
RD_KAFKA_MSG_F_COPY = 0x2
|
260
273
|
|
261
274
|
attach_function :rd_kafka_producev, [:pointer, :varargs], :int, blocking: true
|
275
|
+
attach_function :rd_kafka_purge, [:pointer, :int], :int, blocking: true
|
262
276
|
callback :delivery_cb, [:pointer, :pointer, :pointer], :void
|
263
277
|
attach_function :rd_kafka_conf_set_dr_msg_cb, [:pointer, :delivery_cb], :void
|
278
|
+
attach_function :rd_kafka_init_transactions, [:pointer, :int], :pointer, blocking: true
|
279
|
+
attach_function :rd_kafka_begin_transaction, [:pointer], :pointer, blocking: true
|
280
|
+
attach_function :rd_kafka_abort_transaction, [:pointer, :int], :pointer, blocking: true
|
281
|
+
attach_function :rd_kafka_commit_transaction, [:pointer, :int], :pointer, blocking: true
|
264
282
|
|
265
283
|
# Partitioner
|
266
284
|
PARTITIONERS = %w(random consistent consistent_random murmur2 murmur2_random fnv1a fnv1a_random).each_with_object({}) do |name, hsh|
|
data/lib/rdkafka/config.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "logger"
|
4
|
-
|
5
3
|
module Rdkafka
|
6
4
|
# Configuration for a Kafka consumer or producer. You can create an instance and use
|
7
5
|
# the consumer and producer methods to create a client. Documentation of the available
|
8
|
-
# configuration options is available on https://github.com/
|
6
|
+
# configuration options is available on https://github.com/confluentinc/librdkafka/blob/master/CONFIGURATION.md.
|
9
7
|
class Config
|
10
8
|
# @private
|
11
9
|
@@logger = Logger.new(STDOUT)
|
@@ -14,7 +12,7 @@ module Rdkafka
|
|
14
12
|
# @private
|
15
13
|
@@error_callback = nil
|
16
14
|
# @private
|
17
|
-
@@opaques =
|
15
|
+
@@opaques = ObjectSpace::WeakMap.new
|
18
16
|
# @private
|
19
17
|
@@log_queue = Queue.new
|
20
18
|
|
@@ -53,13 +51,13 @@ module Rdkafka
|
|
53
51
|
|
54
52
|
# Set a callback that will be called every time the underlying client emits statistics.
|
55
53
|
# You can configure if and how often this happens using `statistics.interval.ms`.
|
56
|
-
# The callback is called with a hash that's documented here: https://github.com/
|
54
|
+
# The callback is called with a hash that's documented here: https://github.com/confluentinc/librdkafka/blob/master/STATISTICS.md
|
57
55
|
#
|
58
56
|
# @param callback [Proc, #call] The callback
|
59
57
|
#
|
60
58
|
# @return [nil]
|
61
59
|
def self.statistics_callback=(callback)
|
62
|
-
raise TypeError.new("Callback has to be callable") unless callback.respond_to?(:call)
|
60
|
+
raise TypeError.new("Callback has to be callable") unless callback.respond_to?(:call) || callback == nil
|
63
61
|
@@statistics_callback = callback
|
64
62
|
end
|
65
63
|
|
@@ -114,6 +112,7 @@ module Rdkafka
|
|
114
112
|
def initialize(config_hash = {})
|
115
113
|
@config_hash = DEFAULT_CONFIG.merge(config_hash)
|
116
114
|
@consumer_rebalance_listener = nil
|
115
|
+
@consumer_poll_set = true
|
117
116
|
end
|
118
117
|
|
119
118
|
# Set a config option.
|
@@ -142,12 +141,28 @@ module Rdkafka
|
|
142
141
|
@consumer_rebalance_listener = listener
|
143
142
|
end
|
144
143
|
|
145
|
-
#
|
144
|
+
# Should we use a single queue for the underlying consumer and events.
|
146
145
|
#
|
147
|
-
#
|
148
|
-
#
|
146
|
+
# This is an advanced API that allows for more granular control of the polling process.
|
147
|
+
# When this value is set to `false` (`true` by defualt), there will be two queues that need to
|
148
|
+
# be polled:
|
149
|
+
# - main librdkafka queue for events
|
150
|
+
# - consumer queue with messages and rebalances
|
151
|
+
#
|
152
|
+
# It is recommended to use the defaults and only set it to `false` in advance multi-threaded
|
153
|
+
# and complex cases where granular events handling control is needed.
|
154
|
+
#
|
155
|
+
# @param poll_set [Boolean]
|
156
|
+
def consumer_poll_set=(poll_set)
|
157
|
+
@consumer_poll_set = poll_set
|
158
|
+
end
|
159
|
+
|
160
|
+
# Creates a consumer with this configuration.
|
149
161
|
#
|
150
162
|
# @return [Consumer] The created consumer
|
163
|
+
#
|
164
|
+
# @raise [ConfigError] When the configuration contains invalid options
|
165
|
+
# @raise [ClientCreationError] When the native client cannot be created
|
151
166
|
def consumer
|
152
167
|
opaque = Opaque.new
|
153
168
|
config = native_config(opaque)
|
@@ -160,19 +175,25 @@ module Rdkafka
|
|
160
175
|
# Create native client
|
161
176
|
kafka = native_kafka(config, :rd_kafka_consumer)
|
162
177
|
|
163
|
-
# Redirect the main queue to the consumer
|
164
|
-
Rdkafka::Bindings.rd_kafka_poll_set_consumer(kafka)
|
178
|
+
# Redirect the main queue to the consumer queue
|
179
|
+
Rdkafka::Bindings.rd_kafka_poll_set_consumer(kafka) if @consumer_poll_set
|
165
180
|
|
166
181
|
# Return consumer with Kafka client
|
167
|
-
Rdkafka::Consumer.new(
|
182
|
+
Rdkafka::Consumer.new(
|
183
|
+
Rdkafka::NativeKafka.new(
|
184
|
+
kafka,
|
185
|
+
run_polling_thread: false,
|
186
|
+
opaque: opaque
|
187
|
+
)
|
188
|
+
)
|
168
189
|
end
|
169
190
|
|
170
191
|
# Create a producer with this configuration.
|
171
192
|
#
|
193
|
+
# @return [Producer] The created producer
|
194
|
+
#
|
172
195
|
# @raise [ConfigError] When the configuration contains invalid options
|
173
196
|
# @raise [ClientCreationError] When the native client cannot be created
|
174
|
-
#
|
175
|
-
# @return [Producer] The created producer
|
176
197
|
def producer
|
177
198
|
# Create opaque
|
178
199
|
opaque = Opaque.new
|
@@ -182,22 +203,35 @@ module Rdkafka
|
|
182
203
|
Rdkafka::Bindings.rd_kafka_conf_set_dr_msg_cb(config, Rdkafka::Callbacks::DeliveryCallbackFunction)
|
183
204
|
# Return producer with Kafka client
|
184
205
|
partitioner_name = self[:partitioner] || self["partitioner"]
|
185
|
-
Rdkafka::Producer.new(
|
206
|
+
Rdkafka::Producer.new(
|
207
|
+
Rdkafka::NativeKafka.new(
|
208
|
+
native_kafka(config, :rd_kafka_producer),
|
209
|
+
run_polling_thread: true,
|
210
|
+
opaque: opaque
|
211
|
+
),
|
212
|
+
partitioner_name
|
213
|
+
).tap do |producer|
|
186
214
|
opaque.producer = producer
|
187
215
|
end
|
188
216
|
end
|
189
217
|
|
190
|
-
#
|
218
|
+
# Creates an admin instance with this configuration.
|
219
|
+
#
|
220
|
+
# @return [Admin] The created admin instance
|
191
221
|
#
|
192
222
|
# @raise [ConfigError] When the configuration contains invalid options
|
193
223
|
# @raise [ClientCreationError] When the native client cannot be created
|
194
|
-
#
|
195
|
-
# @return [Admin] The created admin instance
|
196
224
|
def admin
|
197
225
|
opaque = Opaque.new
|
198
226
|
config = native_config(opaque)
|
199
227
|
Rdkafka::Bindings.rd_kafka_conf_set_background_event_cb(config, Rdkafka::Callbacks::BackgroundEventCallbackFunction)
|
200
|
-
Rdkafka::Admin.new(
|
228
|
+
Rdkafka::Admin.new(
|
229
|
+
Rdkafka::NativeKafka.new(
|
230
|
+
native_kafka(config, :rd_kafka_producer),
|
231
|
+
run_polling_thread: true,
|
232
|
+
opaque: opaque
|
233
|
+
)
|
234
|
+
)
|
201
235
|
end
|
202
236
|
|
203
237
|
# Error that is returned by the underlying rdkafka error if an invalid configuration option is present.
|
@@ -18,13 +18,11 @@ module Rdkafka
|
|
18
18
|
|
19
19
|
# Reads a librdkafka native message's headers and returns them as a Ruby Hash
|
20
20
|
#
|
21
|
-
# @
|
21
|
+
# @private
|
22
22
|
#
|
23
|
+
# @param [librdkakfa message] native_message
|
23
24
|
# @return [Hash<String, String>] headers Hash for the native_message
|
24
|
-
#
|
25
25
|
# @raise [Rdkafka::RdkafkaError] when fail to read headers
|
26
|
-
#
|
27
|
-
# @private
|
28
26
|
def self.from_native(native_message)
|
29
27
|
headers_ptrptr = FFI::MemoryPointer.new(:pointer)
|
30
28
|
err = Rdkafka::Bindings.rd_kafka_message_headers(native_message, headers_ptrptr)
|