rdkafka 0.7.0 → 0.8.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +13 -1
- data/CHANGELOG.md +10 -2
- data/docker-compose.yml +15 -11
- data/ext/README.md +3 -15
- data/ext/Rakefile +23 -3
- data/lib/rdkafka.rb +1 -0
- data/lib/rdkafka/bindings.rb +20 -2
- data/lib/rdkafka/config.rb +2 -4
- data/lib/rdkafka/consumer.rb +94 -43
- data/lib/rdkafka/consumer/headers.rb +7 -5
- data/lib/rdkafka/consumer/topic_partition_list.rb +4 -14
- data/lib/rdkafka/error.rb +5 -0
- data/lib/rdkafka/metadata.rb +91 -0
- data/lib/rdkafka/producer.rb +25 -4
- data/lib/rdkafka/producer/delivery_report.rb +6 -1
- data/lib/rdkafka/version.rb +3 -3
- data/rdkafka.gemspec +1 -1
- data/spec/rdkafka/bindings_spec.rb +20 -2
- data/spec/rdkafka/config_spec.rb +6 -2
- data/spec/rdkafka/consumer/message_spec.rb +6 -1
- data/spec/rdkafka/consumer_spec.rb +24 -17
- data/spec/rdkafka/error_spec.rb +3 -3
- data/spec/rdkafka/producer/delivery_report_spec.rb +5 -1
- data/spec/rdkafka/producer_spec.rb +74 -15
- data/spec/spec_helper.rb +14 -3
- metadata +8 -8
data/spec/rdkafka/error_spec.rb
CHANGED
@@ -71,15 +71,15 @@ describe Rdkafka::RdkafkaError do
|
|
71
71
|
end
|
72
72
|
|
73
73
|
it "should not equal another error with a different error code" do
|
74
|
-
expect(subject).
|
74
|
+
expect(subject).not_to eq Rdkafka::RdkafkaError.new(20, "Error explanation")
|
75
75
|
end
|
76
76
|
|
77
77
|
it "should not equal another error with a different message" do
|
78
|
-
expect(subject).
|
78
|
+
expect(subject).not_to eq Rdkafka::RdkafkaError.new(10, "Different error explanation")
|
79
79
|
end
|
80
80
|
|
81
81
|
it "should not equal another error with no message" do
|
82
|
-
expect(subject).
|
82
|
+
expect(subject).not_to eq Rdkafka::RdkafkaError.new(10)
|
83
83
|
end
|
84
84
|
end
|
85
85
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe Rdkafka::Producer::DeliveryReport do
|
4
|
-
subject { Rdkafka::Producer::DeliveryReport.new(2, 100) }
|
4
|
+
subject { Rdkafka::Producer::DeliveryReport.new(2, 100, "error") }
|
5
5
|
|
6
6
|
it "should get the partition" do
|
7
7
|
expect(subject.partition).to eq 2
|
@@ -10,4 +10,8 @@ describe Rdkafka::Producer::DeliveryReport do
|
|
10
10
|
it "should get the offset" do
|
11
11
|
expect(subject.offset).to eq 100
|
12
12
|
end
|
13
|
+
|
14
|
+
it "should get the error" do
|
15
|
+
expect(subject.error).to eq "error"
|
16
|
+
end
|
13
17
|
end
|
@@ -2,10 +2,13 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe Rdkafka::Producer do
|
4
4
|
let(:producer) { rdkafka_config.producer }
|
5
|
+
let(:consumer) { rdkafka_config.consumer }
|
5
6
|
|
6
7
|
after do
|
7
8
|
# Registry should always end up being empty
|
8
9
|
expect(Rdkafka::Producer::DeliveryHandle::REGISTRY).to be_empty
|
10
|
+
producer.close
|
11
|
+
consumer.close
|
9
12
|
end
|
10
13
|
|
11
14
|
context "delivery callback" do
|
@@ -27,6 +30,7 @@ describe Rdkafka::Producer do
|
|
27
30
|
it "should call the callback when a message is delivered" do
|
28
31
|
@callback_called = false
|
29
32
|
|
33
|
+
|
30
34
|
producer.delivery_callback = lambda do |report|
|
31
35
|
expect(report).not_to be_nil
|
32
36
|
expect(report.partition).to eq 1
|
@@ -42,7 +46,10 @@ describe Rdkafka::Producer do
|
|
42
46
|
)
|
43
47
|
|
44
48
|
# Wait for it to be delivered
|
45
|
-
handle.wait(max_wait_timeout:
|
49
|
+
handle.wait(max_wait_timeout: 15)
|
50
|
+
|
51
|
+
# Join the producer thread.
|
52
|
+
producer.close
|
46
53
|
|
47
54
|
# Callback should have been called
|
48
55
|
expect(@callback_called).to be true
|
@@ -55,7 +62,7 @@ describe Rdkafka::Producer do
|
|
55
62
|
payload: "payload",
|
56
63
|
key: "key"
|
57
64
|
)
|
58
|
-
}.to raise_error ArgumentError,
|
65
|
+
}.to raise_error ArgumentError, /missing keyword: [\:]?topic/
|
59
66
|
end
|
60
67
|
|
61
68
|
it "should produce a message" do
|
@@ -82,14 +89,15 @@ describe Rdkafka::Producer do
|
|
82
89
|
# Consume message and verify it's content
|
83
90
|
message = wait_for_message(
|
84
91
|
topic: "produce_test_topic",
|
85
|
-
delivery_report: report
|
92
|
+
delivery_report: report,
|
93
|
+
consumer: consumer
|
86
94
|
)
|
87
95
|
expect(message.partition).to eq 1
|
88
96
|
expect(message.payload).to eq "payload"
|
89
97
|
expect(message.key).to eq "key"
|
90
98
|
# Since api.version.request is on by default we will get
|
91
99
|
# the message creation timestamp if it's not set.
|
92
|
-
expect(message.timestamp).to be_within(
|
100
|
+
expect(message.timestamp).to be_within(10).of(Time.now)
|
93
101
|
end
|
94
102
|
|
95
103
|
it "should produce a message with a specified partition" do
|
@@ -105,12 +113,47 @@ describe Rdkafka::Producer do
|
|
105
113
|
# Consume message and verify it's content
|
106
114
|
message = wait_for_message(
|
107
115
|
topic: "produce_test_topic",
|
108
|
-
delivery_report: report
|
116
|
+
delivery_report: report,
|
117
|
+
consumer: consumer
|
109
118
|
)
|
110
119
|
expect(message.partition).to eq 1
|
111
120
|
expect(message.key).to eq "key partition"
|
112
121
|
end
|
113
122
|
|
123
|
+
it "should produce a message to the same partition with a similar partition key" do
|
124
|
+
# Avoid partitioner collisions.
|
125
|
+
while true
|
126
|
+
key = ('a'..'z').to_a.shuffle.take(10).join('')
|
127
|
+
partition_key = ('a'..'z').to_a.shuffle.take(10).join('')
|
128
|
+
partition_count = producer.partition_count('partitioner_test_topic')
|
129
|
+
break if (Zlib.crc32(key) % partition_count) != (Zlib.crc32(partition_key) % partition_count)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Produce a message with key, partition_key and key + partition_key
|
133
|
+
messages = [{key: key}, {partition_key: partition_key}, {key: key, partition_key: partition_key}]
|
134
|
+
|
135
|
+
messages = messages.map do |m|
|
136
|
+
handle = producer.produce(
|
137
|
+
topic: "partitioner_test_topic",
|
138
|
+
payload: "payload partition",
|
139
|
+
key: m[:key],
|
140
|
+
partition_key: m[:partition_key]
|
141
|
+
)
|
142
|
+
report = handle.wait(max_wait_timeout: 5)
|
143
|
+
|
144
|
+
wait_for_message(
|
145
|
+
topic: "partitioner_test_topic",
|
146
|
+
delivery_report: report,
|
147
|
+
)
|
148
|
+
end
|
149
|
+
|
150
|
+
expect(messages[0].partition).not_to eq(messages[2].partition)
|
151
|
+
expect(messages[1].partition).to eq(messages[2].partition)
|
152
|
+
expect(messages[0].key).to eq key
|
153
|
+
expect(messages[1].key).to be_nil
|
154
|
+
expect(messages[2].key).to eq key
|
155
|
+
end
|
156
|
+
|
114
157
|
it "should produce a message with utf-8 encoding" do
|
115
158
|
handle = producer.produce(
|
116
159
|
topic: "produce_test_topic",
|
@@ -122,7 +165,8 @@ describe Rdkafka::Producer do
|
|
122
165
|
# Consume message and verify it's content
|
123
166
|
message = wait_for_message(
|
124
167
|
topic: "produce_test_topic",
|
125
|
-
delivery_report: report
|
168
|
+
delivery_report: report,
|
169
|
+
consumer: consumer
|
126
170
|
)
|
127
171
|
|
128
172
|
expect(message.partition).to eq 1
|
@@ -154,7 +198,8 @@ describe Rdkafka::Producer do
|
|
154
198
|
# Consume message and verify it's content
|
155
199
|
message = wait_for_message(
|
156
200
|
topic: "produce_test_topic",
|
157
|
-
delivery_report: report
|
201
|
+
delivery_report: report,
|
202
|
+
consumer: consumer
|
158
203
|
)
|
159
204
|
|
160
205
|
expect(message.partition).to eq 2
|
@@ -174,7 +219,8 @@ describe Rdkafka::Producer do
|
|
174
219
|
# Consume message and verify it's content
|
175
220
|
message = wait_for_message(
|
176
221
|
topic: "produce_test_topic",
|
177
|
-
delivery_report: report
|
222
|
+
delivery_report: report,
|
223
|
+
consumer: consumer
|
178
224
|
)
|
179
225
|
|
180
226
|
expect(message.partition).to eq 2
|
@@ -193,7 +239,8 @@ describe Rdkafka::Producer do
|
|
193
239
|
# Consume message and verify it's content
|
194
240
|
message = wait_for_message(
|
195
241
|
topic: "produce_test_topic",
|
196
|
-
delivery_report: report
|
242
|
+
delivery_report: report,
|
243
|
+
consumer: consumer
|
197
244
|
)
|
198
245
|
|
199
246
|
expect(message.key).to be_nil
|
@@ -210,7 +257,8 @@ describe Rdkafka::Producer do
|
|
210
257
|
# Consume message and verify it's content
|
211
258
|
message = wait_for_message(
|
212
259
|
topic: "produce_test_topic",
|
213
|
-
delivery_report: report
|
260
|
+
delivery_report: report,
|
261
|
+
consumer: consumer
|
214
262
|
)
|
215
263
|
|
216
264
|
expect(message.key).to eq "key no payload"
|
@@ -229,7 +277,8 @@ describe Rdkafka::Producer do
|
|
229
277
|
# Consume message and verify it's content
|
230
278
|
message = wait_for_message(
|
231
279
|
topic: "produce_test_topic",
|
232
|
-
delivery_report: report
|
280
|
+
delivery_report: report,
|
281
|
+
consumer: consumer
|
233
282
|
)
|
234
283
|
|
235
284
|
expect(message.payload).to eq "payload headers"
|
@@ -251,7 +300,8 @@ describe Rdkafka::Producer do
|
|
251
300
|
# Consume message and verify it's content
|
252
301
|
message = wait_for_message(
|
253
302
|
topic: "produce_test_topic",
|
254
|
-
delivery_report: report
|
303
|
+
delivery_report: report,
|
304
|
+
consumer: consumer
|
255
305
|
)
|
256
306
|
|
257
307
|
expect(message.payload).to eq "payload headers"
|
@@ -284,11 +334,17 @@ describe Rdkafka::Producer do
|
|
284
334
|
# Fork, produce a message, send the report over a pipe and
|
285
335
|
# wait for and check the message in the main process.
|
286
336
|
|
337
|
+
# Kernel#fork is not available in JRuby
|
338
|
+
skip if defined?(JRUBY_VERSION)
|
339
|
+
|
287
340
|
reader, writer = IO.pipe
|
288
341
|
|
289
342
|
fork do
|
290
343
|
reader.close
|
291
344
|
|
345
|
+
# Avoids sharing the socket between processes.
|
346
|
+
producer = rdkafka_config.producer
|
347
|
+
|
292
348
|
handle = producer.produce(
|
293
349
|
topic: "produce_test_topic",
|
294
350
|
payload: "payload-forked",
|
@@ -296,7 +352,6 @@ describe Rdkafka::Producer do
|
|
296
352
|
)
|
297
353
|
|
298
354
|
report = handle.wait(max_wait_timeout: 5)
|
299
|
-
producer.close
|
300
355
|
|
301
356
|
report_json = JSON.generate(
|
302
357
|
"partition" => report.partition,
|
@@ -304,20 +359,24 @@ describe Rdkafka::Producer do
|
|
304
359
|
)
|
305
360
|
|
306
361
|
writer.write(report_json)
|
362
|
+
writer.close
|
363
|
+
producer.close
|
307
364
|
end
|
308
365
|
|
309
366
|
writer.close
|
310
|
-
|
311
367
|
report_hash = JSON.parse(reader.read)
|
312
368
|
report = Rdkafka::Producer::DeliveryReport.new(
|
313
369
|
report_hash["partition"],
|
314
370
|
report_hash["offset"]
|
315
371
|
)
|
316
372
|
|
373
|
+
reader.close
|
374
|
+
|
317
375
|
# Consume message and verify it's content
|
318
376
|
message = wait_for_message(
|
319
377
|
topic: "produce_test_topic",
|
320
|
-
delivery_report: report
|
378
|
+
delivery_report: report,
|
379
|
+
consumer: consumer
|
321
380
|
)
|
322
381
|
expect(message.partition).to eq 0
|
323
382
|
expect(message.payload).to eq "payload-forked"
|
data/spec/spec_helper.rb
CHANGED
@@ -7,6 +7,14 @@ require "pry"
|
|
7
7
|
require "rspec"
|
8
8
|
require "rdkafka"
|
9
9
|
|
10
|
+
`docker-compose exec kafka kafka-topics --create --zookeeper zookeeper:2181 --replication-factor 1 --partitions 3 --if-not-exists --topic consume_test_topic`
|
11
|
+
`docker-compose exec kafka kafka-topics --create --zookeeper zookeeper:2181 --replication-factor 1 --partitions 3 --if-not-exists --topic empty_test_topic`
|
12
|
+
`docker-compose exec kafka kafka-topics --create --zookeeper zookeeper:2181 --replication-factor 1 --partitions 3 --if-not-exists --topic load_test_topic`
|
13
|
+
`docker-compose exec kafka kafka-topics --create --zookeeper zookeeper:2181 --replication-factor 1 --partitions 3 --if-not-exists --topic produce_test_topic`
|
14
|
+
`docker-compose exec kafka kafka-topics --create --zookeeper zookeeper:2181 --replication-factor 1 --partitions 3 --if-not-exists --topic rake_test_topic`
|
15
|
+
`docker-compose exec kafka kafka-topics --create --zookeeper zookeeper:2181 --replication-factor 1 --partitions 3 --if-not-exists --topic watermarks_test_topic`
|
16
|
+
`docker-compose exec kafka kafka-topics --create --zookeeper zookeeper:2181 --replication-factor 1 --partitions 25 --if-not-exists --topic partitioner_test_topic`
|
17
|
+
|
10
18
|
def rdkafka_config(config_overrides={})
|
11
19
|
config = {
|
12
20
|
:"api.version.request" => false,
|
@@ -25,12 +33,12 @@ def rdkafka_config(config_overrides={})
|
|
25
33
|
Rdkafka::Config.new(config)
|
26
34
|
end
|
27
35
|
|
28
|
-
def
|
36
|
+
def new_native_client
|
29
37
|
config = rdkafka_config
|
30
38
|
config.send(:native_kafka, config.send(:native_config), :rd_kafka_producer)
|
31
39
|
end
|
32
40
|
|
33
|
-
def new_native_topic(topic_name="topic_name")
|
41
|
+
def new_native_topic(topic_name="topic_name", native_client: )
|
34
42
|
Rdkafka::Bindings.rd_kafka_topic_new(
|
35
43
|
native_client,
|
36
44
|
topic_name,
|
@@ -39,7 +47,8 @@ def new_native_topic(topic_name="topic_name")
|
|
39
47
|
end
|
40
48
|
|
41
49
|
def wait_for_message(topic:, delivery_report:, timeout_in_seconds: 30, consumer: nil)
|
42
|
-
|
50
|
+
new_consumer = !!consumer
|
51
|
+
consumer ||= rdkafka_config.consumer
|
43
52
|
consumer.subscribe(topic)
|
44
53
|
timeout = Time.now.to_i + timeout_in_seconds
|
45
54
|
loop do
|
@@ -53,6 +62,8 @@ def wait_for_message(topic:, delivery_report:, timeout_in_seconds: 30, consumer:
|
|
53
62
|
return message
|
54
63
|
end
|
55
64
|
end
|
65
|
+
ensure
|
66
|
+
consumer.close if new_consumer
|
56
67
|
end
|
57
68
|
|
58
69
|
def wait_for_assignment(consumer)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rdkafka
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0.beta.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thijs Cadier
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-04-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -42,14 +42,14 @@ dependencies:
|
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '12.3'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '12.3'
|
55
55
|
- !ruby/object:Gem::Dependency
|
@@ -136,6 +136,7 @@ files:
|
|
136
136
|
- lib/rdkafka/consumer/partition.rb
|
137
137
|
- lib/rdkafka/consumer/topic_partition_list.rb
|
138
138
|
- lib/rdkafka/error.rb
|
139
|
+
- lib/rdkafka/metadata.rb
|
139
140
|
- lib/rdkafka/producer.rb
|
140
141
|
- lib/rdkafka/producer/delivery_handle.rb
|
141
142
|
- lib/rdkafka/producer/delivery_report.rb
|
@@ -167,12 +168,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
167
168
|
version: '2.4'
|
168
169
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
170
|
requirements:
|
170
|
-
- - "
|
171
|
+
- - ">"
|
171
172
|
- !ruby/object:Gem::Version
|
172
|
-
version:
|
173
|
+
version: 1.3.1
|
173
174
|
requirements: []
|
174
|
-
|
175
|
-
rubygems_version: 2.7.6.2
|
175
|
+
rubygems_version: 3.1.2
|
176
176
|
signing_key:
|
177
177
|
specification_version: 4
|
178
178
|
summary: The rdkafka gem is a modern Kafka client library for Ruby based on librdkafka.
|